API Reference¶
Complete auto-generated API documentation from docstrings.
Main Package¶
The main stellium package exports the most commonly used classes and functions.
Builders¶
- class stellium.ChartBuilder(datetime, location, native=None)[source]¶
Bases:
objectFluent builder for creating astrological charts.
Example:
chart = ( ChartBuilder.from_native(native) .with_ephemeris(SwissEphemeris()) .with_house_systems([PlacidusHouses(), WholeSignHouses()]) .with_aspects(ModernAspectEngine()) .with_orbs(SimpleOrbEngine()) .calculate() )
- add_analyzer(analyzer)[source]¶
Adds a data analyzer to the calculation pipeline. (e.g., PatternDetector)
- Return type:
- add_component(component)[source]¶
Add an additional calculation component (e.g. ArabicParts).
- Return type:
- add_house_system(engine)[source]¶
Adds an additional house engine to the calculation list. (e.g., to calculate Placidus and Whole Sign)
- Return type:
- bazi()[source]¶
Calculate the BaZi (Four Pillars / 八字) chart directly from the builder.
Skips Western chart calculation entirely — uses the already-resolved datetime and location to compute Chinese Four Pillars.
- Returns:
A BaZiChart with all four pillars, ready for analysis.
Example:
bazi = ChartBuilder.from_details("1994-01-06 11:47", "Palo Alto, CA").bazi() print(bazi.hanzi) # Eight characters print(bazi.strength()) # Strength analysis
- calculate()[source]¶
Execute all calculations and return the final chart.
- Return type:
- Returns:
CalculatedChart with all calculated data, or UnknownTimeChart if time_unknown flag is set
- classmethod from_details(datetime_input, location_input, *, name=None, time_unknown=False)[source]¶
Create a ChartBuilder from datetime and location (convenience method).
This method accepts flexible datetime and location inputs, creates a Native object internally, and returns a ready-to-configure ChartBuilder.
- Parameters:
datetime_input (
str|datetime|dict) – Datetime as string, datetime object, or dict - String: “2024-11-24 14:30”, “11/24/2024 2:30 PM”, etc. - datetime: Any datetime object (naive will be localized to location) - dict: {“year”: 2024, “month”: 11, “day”: 24, “hour”: 14, “minute”: 30}location_input (
str|tuple[float,float] |dict) – Location as string, (lat, lon) tuple, or dict - String: “Palo Alto, CA” (will be geocoded) - Tuple: (37.4419, -122.1430) - dict: {“latitude”: 37.4419, “longitude”: -122.1430, “name”: “Palo Alto”}name (
str|None) – Optional name of the person or event (for display purposes)time_unknown (
bool) – If True, creates an UnknownTimeChart (no houses/angles, Moon shown as range, time normalized to noon)
- Return type:
- Returns:
ChartBuilder instance ready to configure
Examples
>>> # Simple string inputs >>> chart = ChartBuilder.from_details( ... "1994-01-06 11:47", ... "Palo Alto, CA" ... ).calculate() >>> >>> # With a name >>> chart = ChartBuilder.from_details( ... "1994-01-06 11:47", ... "Palo Alto, CA", ... name="Kate Louie" ... ).calculate() >>> >>> # Unknown birth time >>> chart = ChartBuilder.from_details( ... "1994-01-06", ... "Palo Alto, CA", ... name="Someone", ... time_unknown=True ... ).calculate()
- classmethod from_native(native)[source]¶
Create a new ChartBuilder from a Native object.
This is the primary factory method.
- Return type:
- classmethod from_notable(name)[source]¶
Create a ChartBuilder from the notable registry by name.
This is a convenience method that looks up a famous birth or event from the curated registry and creates a chart for it.
The notable’s name is automatically set on the chart for display purposes.
- Parameters:
name (
str) – Name of person or event (case-insensitive)- Return type:
- Returns:
ChartBuilder instance ready to build, with name pre-set
- Raises:
ValueError – If name not found in registry
Example
>>> chart = ChartBuilder.from_notable("Albert Einstein").calculate() >>> chart = ChartBuilder.from_notable("marie curie").calculate()
- with_cache(cache=None, enabled=True, cache_dir='.cache', max_age_seconds=86400)[source]¶
Configure caching for this chart calculation.
- Parameters:
- Return type:
- Returns:
Self for chaining
Examples
# Disable caching for this chart chart = ChartBuilder.from_native(native).with_cache(enabled=False).calculate()
# Use custom cache directory chart = ChartBuilder.from_native(native).with_cache(cache_dir=”/tmp/my_cache”).calculate()
# Use shared cache instance my_cache = Cache(cache_dir=”/shared/cache”) chart1 = ChartBuilder.from_native(n1).with_cache(cache=my_cache).calculate() chart2 = ChartBuilder.from_native(n2).with_cache(cache=my_cache).calculate()
- with_config(config)[source]¶
Set the calculation configuration (which objects to find).
- Return type:
- with_declination_aspects(orb=1.0, include_types=None)[source]¶
Enable declination aspect calculation (Parallel/Contraparallel).
Declination aspects are based on equatorial coordinates rather than ecliptic longitude. They use a tighter orb (default 1.0°) than longitude-based aspects.
Parallel: Two bodies at the same declination (same hemisphere). Interpreted like a conjunction.
Contraparallel: Two bodies at equal declination but opposite hemispheres. Interpreted like an opposition.
- Parameters:
- Return type:
- Returns:
Self for chaining
Example
>>> chart = (ChartBuilder.from_native(native) ... .with_aspects() ... .with_declination_aspects(orb=1.0) ... .calculate()) >>> for asp in chart.declination_aspects: ... print(asp.description) >>> parallels = chart.get_parallels() >>> contraparallels = chart.get_contraparallels()
- with_heliocentric()[source]¶
Use heliocentric (Sun-centered) coordinates.
In a heliocentric chart, positions are calculated as seen from the Sun rather than Earth. This changes the chart significantly:
Earth appears as a planet (replacing the Sun)
Sun is removed (it’s the center point)
Lunar nodes and apogees are removed (Earth-relative concepts)
Moon is kept (still orbits Earth, has heliocentric position)
Houses and angles are not calculated (Earth-horizon concepts)
Heliocentric charts are used in: - Financial astrology (market timing) - Some modern experimental techniques - Scientific/astronomical contexts
- Return type:
- Returns:
Self for method chaining
Example
>>> chart = (ChartBuilder.from_native(native) ... .with_heliocentric() ... .calculate()) >>> earth = chart.get_object("Earth") >>> print(earth.sign_position) # Where Earth is from the Sun's view
- with_house_systems(engines)[source]¶
Replaces the entire list of house engines (eg - to calculate only Whole Sign)
- Return type:
- with_name(name)[source]¶
Set the chart name (for display purposes).
- Parameters:
name (
str) – Name to display on the chart (e.g., person’s name, event name)- Return type:
- Returns:
Self for method chaining
Example
>>> chart = (ChartBuilder.from_native(native) ... .with_name("John Doe") ... .calculate())
- with_sidereal(ayanamsa='lahiri')[source]¶
Use sidereal zodiac for calculations.
The sidereal zodiac is based on fixed star positions, unlike the tropical zodiac which is based on the seasons. Different ayanamsa systems represent different methods of calculating the offset between tropical and sidereal.
- Parameters:
ayanamsa (
str) – The ayanamsa system to use. Common options: - “lahiri” (default) - Indian government standard, most common for Vedic - “fagan_bradley” - Primary Western sidereal - “raman” - B.V. Raman’s system, popular in South India - “krishnamurti” - Used in KP system - “yukteshwar” - Sri Yukteshwar’s system See stellium.core.ayanamsa.list_ayanamsas() for all options- Return type:
- Returns:
Self for method chaining
Example
>>> # Vedic-style chart with Lahiri ayanamsa >>> chart = (ChartBuilder.from_native(native) ... .with_sidereal("lahiri") ... .calculate()) >>> >>> # Western sidereal with Fagan-Bradley >>> chart = (ChartBuilder.from_native(native) ... .with_sidereal("fagan_bradley") ... .calculate())
- with_tnos()[source]¶
Include Trans-Neptunian Objects in the calculation.
Adds the major TNOs: - Eris (dwarf planet, discord) - Sedna (isolation, deep healing) - Makemake (resourcefulness, manifestation) - Haumea (rebirth, fertility) - Orcus (oaths, consequences) - Quaoar (creation, harmony)
Note: TNOs require additional Swiss Ephemeris asteroid files (se1 files) to be present in your ephemeris data directory. Download them from: https://www.astro.com/ftp/swisseph/ephe/
Example:
chart = ChartBuilder.from_native(native).with_tnos().calculate()
- Return type:
- with_tropical()[source]¶
Use tropical zodiac for calculations (default).
The tropical zodiac is based on the seasons, with 0° Aries aligned to the March equinox. This is the standard system used in Western astrology.
This method is included for explicitness - tropical is already the default, so you only need to call this if you want to override a previous .with_sidereal() call.
- Return type:
- Returns:
Self for method chaining
Example
>>> # Explicit tropical (same as default) >>> chart = (ChartBuilder.from_native(native) ... .with_tropical() ... .calculate()) >>> >>> # Override previous sidereal setting >>> chart = (ChartBuilder.from_native(native) ... .with_sidereal("lahiri") ... .with_tropical() # Back to tropical ... .calculate())
- with_unknown_time()[source]¶
Mark this chart as having unknown birth time.
When birth time is unknown: - Time is normalized to noon for planet calculations - Houses and angles will NOT be calculated - Moon will include a range showing possible positions throughout the day - The resulting chart is an UnknownTimeChart (subclass of CalculatedChart)
- Return type:
- Returns:
Self for method chaining
Example
>>> chart = (ChartBuilder ... .from_details("1994-01-06", "Palo Alto, CA") ... .with_unknown_time() ... .calculate()) >>> isinstance(chart, UnknownTimeChart) True >>> chart.moon_range.arc_size 13.5 # Moon travels ~13.5° that day
- with_uranian()[source]¶
Include Hamburg/Uranian hypothetical planets and points in the calculation.
Adds the 8 transneptunian points (TNPs) used in Uranian astrology: - Cupido (family, groups, art, community) - Hades (decay, the past, what’s hidden) - Zeus (leadership, fire, directed energy) - Kronos (authority, expertise, high position) - Apollon (expansion, science, commerce, success) - Admetos (depth, stagnation, raw materials) - Vulkanus (immense power, force, intensity) - Poseidon (spirituality, enlightenment, clarity)
Also adds the Aries Point (0° Aries), a fundamental reference point in Uranian astrology representing worldly manifestation and the intersection of personal and collective.
These are hypothetical planets developed by Alfred Witte and the Hamburg School of Astrology.
Example:
# Just Uranian planets chart = ChartBuilder.from_native(native).with_uranian().calculate() # Full Uranian setup (TNOs + TNPs) chart = ChartBuilder.from_native(native).with_tnos().with_uranian().calculate()
- Return type:
- class stellium.ComparisonBuilder(chart1, comparison_type, chart1_label='Native')[source]¶
Bases:
objectFluent builder for creating Comparison objects.
Provides convenient construction methods for both synastry and transits:
- For synastry:
- comp = ComparisonBuilder.from_native(chart1)
.with_partner(chart2) .calculate()
- For transits:
- comp = ComparisonBuilder.from_native(natal_chart)
.with_transit(transit_datetime, transit_location) .calculate()
- classmethod arc_direction(natal_data, *, target_date=None, age=None, arc_type='solar_arc', rulership_system='traditional', natal_label='Natal', directed_label='Directed')[source]¶
Create an arc direction comparison (natal vs directed chart).
Arc directions move ALL points by the same angular distance, preserving natal relationships. This differs from progressions where each planet moves at its own rate.
- Arc types supported:
“solar_arc”: Arc = progressed Sun - natal Sun (~1°/year actual)
“naibod”: Arc = 0.9856° × years (mean solar motion)
“lunar”: Arc = progressed Moon - natal Moon (~12-13°/year)
“chart_ruler”: Arc based on planet ruling the Ascendant sign
“sect”: Day charts use solar arc, night charts use lunar arc
Any planet name (e.g., “Mars”, “Venus”): Uses that planet’s arc
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart data (Native, CalculatedChart, or tuple)target_date (
str|datetime|None) – Target date for directions (either this or age required)age (
float|None) – Age in years (alternative to target_date)arc_type (
str) – Type of arc to use (see above)rulership_system (
Literal['traditional','modern']) – “traditional” or “modern” (for chart_ruler arc)natal_label (
str) – Label for natal chart (default: “Natal”)directed_label (
str) – Label for directed chart (default: “Directed”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # Solar arc directions at age 30 >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, age=30, arc_type="solar_arc" ... ).calculate()
>>> # Naibod arc directions to a specific date >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, target_date="2025-06-15", arc_type="naibod" ... ).calculate()
>>> # Chart ruler arc (uses planet ruling ASC sign) >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, age=30, arc_type="chart_ruler" ... ).calculate()
>>> # Sect-based arc (solar for day charts, lunar for night) >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, age=30, arc_type="sect" ... ).calculate()
>>> # Mars arc directions >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, age=30, arc_type="Mars" ... ).calculate()
- calculate()[source]¶
Execute all calculations and return the final Comparison.
This method ensures that both charts have their internal aspects calculated before computing cross-chart aspects. If a chart doesn’t already have aspects, they will be calculated using the internal aspect engine configuration.
- Return type:
- Returns:
Comparison object with all calculated data
- classmethod compare(data1, data2, comparison_type, chart1_label='Chart 1', chart2_label='Chart 2')[source]¶
General method for creating any type of comparison.
This is the flexible method that accepts any combination of inputs and any comparison type. Convenience methods (.synastry(), .transit(), .progression()) are thin wrappers that call this method with appropriate defaults.
- Parameters:
data1 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – First chart data (CalculatedChart, Native, or (datetime, location) tuple)data2 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Second chart data (CalculatedChart, Native, or (datetime, location) tuple)comparison_type (
str) – Type of comparison (“synastry”, “transit”, “progression”)chart1_label (
str) – Label for first chart (default: “Chart 1”)chart2_label (
str) – Label for second chart (default: “Chart 2”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # With Native objects >>> native1 = Native("1994-01-06 11:47", "Palo Alto, CA") >>> native2 = Native("2000-01-01 17:00", "Seattle, WA") >>> comparison = ComparisonBuilder.compare(native1, native2, "synastry").calculate() >>> >>> # With (datetime, location) tuples >>> comparison = ComparisonBuilder.compare( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2000-01-01 17:00", "Seattle, WA"), ... "synastry" ... ).calculate() >>> >>> # Mixed inputs >>> comparison = ComparisonBuilder.compare( ... native1, ... ("2024-11-24 14:30", None), # Uses chart1's location for transits ... "transit" ... ).calculate()
- classmethod from_native(native_chart, native_label='Native')[source]¶
Start building a comparison from a native chart.
Use this when you have a CalculatedChart already. Chain with .with_partner() or .with_transit()
- Parameters:
native_chart (
CalculatedChart) – The native/primary chartnative_label (
str) – Label for the native chart
- Return type:
- Returns:
ComparisonBuilder instance
- classmethod progression(natal_data, progressed_data=None, *, target_date=None, age=None, angle_method='quotidian', natal_label='Natal', progressed_label='Progressed')[source]¶
Create a progression comparison with auto-calculation support.
Secondary progressions use the symbolic equation “one day = one year.” To find progressed positions at age 30, look at where planets were 30 days after birth.
Can be called three ways: 1. Auto-calculate by target date: progression(natal, target_date=”2025-06-15”) 2. Auto-calculate by age: progression(natal, age=30) 3. Manual (legacy): progression(natal, progressed_chart)
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart data (Native, CalculatedChart, or (datetime, location) tuple)progressed_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict] |None) – Optional pre-calculated progressed chart (for backwards compatibility)target_date (
str|datetime|None) – Target date for progression (triggers auto-calculation)age (
float|None) – Age in years for progression (alternative to target_date)angle_method (
Literal['quotidian','solar_arc','naibod']) – How to progress angles: - “quotidian” (default): Actual daily motion from Swiss Ephemeris - “solar_arc”: Angles progress at rate of progressed Sun - “naibod”: Angles progress at mean Sun rate (59’08”/year)natal_label (
str) – Label for natal chart (default: “Natal”)progressed_label (
str) – Label for progressed chart (default: “Progressed”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # Auto-calculate by age (most convenient) >>> prog = ComparisonBuilder.progression(natal, age=30).calculate() >>> >>> # Auto-calculate by target date >>> prog = ComparisonBuilder.progression( ... natal, target_date="2025-06-15" ... ).calculate() >>> >>> # With solar arc angles >>> prog = ComparisonBuilder.progression( ... natal, age=30, angle_method="solar_arc" ... ).calculate() >>> >>> # Legacy: explicit progressed chart (backwards compatible) >>> progressed_chart = ChartBuilder.from_details( ... "1994-02-05 11:47", "Palo Alto, CA" ... ).calculate() >>> prog = ComparisonBuilder.progression(natal, progressed_chart).calculate()
- classmethod synastry(data1, data2, chart1_label='Person 1', chart2_label='Person 2')[source]¶
Create a synastry comparison between two natal charts.
Synastry analyzes the relationship between two people by comparing their birth charts. This is a convenience method that calls .compare() with comparison_type=”synastry”.
- Parameters:
data1 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – First person’s chart data (Native, CalculatedChart, or (datetime, location) tuple)data2 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Second person’s chart data (Native, CalculatedChart, or (datetime, location) tuple)chart1_label (
str) – Label for first person (default: “Person 1”)chart2_label (
str) – Label for second person (default: “Person 2”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # Simple string inputs >>> comparison = ComparisonBuilder.synastry( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2000-01-01 17:00", "Seattle, WA") ... ).calculate() >>> >>> # With Native objects >>> native1 = Native("1994-01-06 11:47", "Palo Alto, CA") >>> native2 = Native("2000-01-01 17:00", "Seattle, WA") >>> comparison = ComparisonBuilder.synastry(native1, native2).calculate() >>> >>> # With custom labels >>> comparison = ComparisonBuilder.synastry( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2000-01-01 17:00", "Seattle, WA"), ... chart1_label="Kate", ... chart2_label="Partner" ... ).calculate()
- classmethod transit(natal_data, transit_data, natal_label='Natal', transit_label='Transit')[source]¶
Create a transit comparison (natal chart vs current sky positions).
Transits analyze how current planetary positions interact with a natal chart for timing and prediction. This is a convenience method that calls .compare() with comparison_type=”transit”.
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart data (Native, CalculatedChart, or (datetime, location) tuple)transit_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict|None]) – Transit time data. Can be: - (datetime, location) tuple - (datetime, None) to use natal location - Native or CalculatedChartnatal_label (
str) – Label for natal chart (default: “Natal”)transit_label (
str) – Label for transit chart (default: “Transit”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # Transit using natal location >>> comparison = ComparisonBuilder.transit( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2024-11-24 14:30", None) # Uses Palo Alto ... ).calculate() >>> >>> # Transit with different location >>> comparison = ComparisonBuilder.transit( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2024-11-24 14:30", "New York, NY") ... ).calculate() >>> >>> # With Native object >>> natal = Native("1994-01-06 11:47", "Palo Alto, CA") >>> comparison = ComparisonBuilder.transit( ... natal, ... ("2024-11-24 14:30", None) ... ).calculate()
- with_aspect_config(aspect_config)[source]¶
Set aspect configuration (orbs, which aspects, etc.).
- Parameters:
aspect_config (
AspectConfig) – AspectConfig instance- Return type:
- Returns:
Self for chaining
- with_aspect_engine(engine)[source]¶
Set the aspect engine for cross-chart aspects.
- Parameters:
engine – AspectEngine instance
- Return type:
- Returns:
Self for chaining
- with_internal_aspect_engine(engine)[source]¶
Set aspect engine for calculating internal (natal) aspects.
This engine will be used to calculate aspects within chart1 and chart2 if they don’t already have aspects calculated. If not set, defaults to ModernAspectEngine().
- Parameters:
engine – AspectEngine instance for internal aspects
- Return type:
- Returns:
Self for chaining
- with_internal_orb_engine(engine)[source]¶
Set orb engine for calculating internal (natal) aspects.
This engine will be used for orb allowances when calculating internal aspects in chart1/chart2. If not set, defaults to SimpleOrbEngine with registry defaults.
- Parameters:
engine (
OrbEngine) – OrbEngine instance for internal aspect orbs- Return type:
- Returns:
Self for chaining
- with_orb_engine(engine)[source]¶
Set the orb calculation engine for dynamic orb calculation.
OrbEngine will be used to calculate orbs for each planet pair dynamically (e.g., wider orbs for Sun/Moon, tighter for fast planets).
If provided, OrbEngine takes precedence over AspectConfig.orbs.
Examples
from stellium.engines.orbs import SimpleOrbEngine, LuminariesOrbEngine
# Simple engine with fixed orbs per aspect simple = SimpleOrbEngine({‘Conjunction’: 8.0, ‘Trine’: 8.0}) builder.with_orb_engine(simple)
# Luminaries engine (wider orbs for Sun/Moon) lum = LuminariesOrbEngine() builder.with_orb_engine(lum)
- Parameters:
engine – OrbEngine instance implementing get_orb_allowance()
- Return type:
- Returns:
Self for chaining
- with_other(other_input, location=None, other_label='Other', comparison_type=None)[source]¶
Generic method to add second chart.
This is a flexible alternative to with_partner() and with_transit().
- Parameters:
other_input (
CalculatedChart|datetime|Native) – Either a CalculatedChart, Native or datetimelocation (
ChartLocation|str|None) – Required if providing datetime. ChartLocation or str place nameother_label (
str) – Label for the other chartcomparison_type (
ComparisonType|None) – Optional comparison type (default: SYNASTRY)
- Return type:
- Returns:
Self for chaining
- with_partner(partner_chart_or_datetime_or_native, location=None, partner_label='Partner')[source]¶
Add partner chart for synastry comparison.
- Parameters:
partner_chart_or_datetime – Either a CalculatedChart or datetime
location (
ChartLocation|None) – Required if providing datetimepartner_label (
str) – Label for the partner chart
- Return type:
- Returns:
Self for chaining
- with_transit(transit_datetime, location=None)[source]¶
Add transit chart for transit comparison.
Convenience method that calls with_other() with appropriate settings.
- Parameters:
transit_datetime (
datetime) – Transit datetimelocation (
ChartLocation|None) – Optional location (defaults to native’s location)
- Return type:
- Returns:
Self for chaining
- class stellium.ReturnBuilder(natal, planet, *, year=None, near_date=None, occurrence=None, location=None)[source]¶
Bases:
objectFluent builder for planetary return charts.
Uses composition: wraps ChartBuilder rather than inheriting from it. This allows us to: - Lazily calculate the return moment before building the inner chart - Inject return-specific metadata into the final chart - Delegate all chainable methods without tight coupling
- Usage:
>>> # Solar Return for 2025 >>> sr = ReturnBuilder.solar(natal_chart, 2025).calculate() >>> >>> # Lunar Return near a date >>> lr = ReturnBuilder.lunar(natal_chart, near_date="2025-03-15").calculate() >>> >>> # First Saturn Return >>> saturn = ReturnBuilder.planetary(natal_chart, "Saturn", occurrence=1).calculate() >>> >>> # Relocated Solar Return >>> sr_relocated = ( ... ReturnBuilder.solar(natal_chart, 2025, location="Tokyo, Japan") ... .calculate() ... )
- calculate()[source]¶
Calculate the return chart.
This: 1. Finds the exact moment of the planetary return 2. Creates a ChartBuilder for that moment 3. Applies any deferred configuration 4. Injects return metadata 5. Returns the calculated chart
- Return type:
- Returns:
CalculatedChart with return metadata in chart.metadata
- classmethod lunar(natal, *, near_date=None, occurrence=None, location=None)[source]¶
Create a Lunar Return builder.
A Lunar Return is the chart cast for when the Moon returns to its exact natal position. This happens approximately every 27.3 days.
- Parameters:
- Return type:
- Returns:
ReturnBuilder configured for Lunar Return
Example
>>> # Lunar Return nearest to March 15, 2025 >>> lr = ReturnBuilder.lunar(natal, near_date="2025-03-15").calculate() >>> >>> # The 100th Lunar Return >>> lr_100 = ReturnBuilder.lunar(natal, occurrence=100).calculate()
- classmethod planetary(natal, planet, *, near_date=None, occurrence=None, location=None)[source]¶
Create a planetary return builder for any planet.
- Parameters:
- Return type:
- Returns:
ReturnBuilder configured for the specified planetary return
Example
>>> # First Saturn Return (~age 29) >>> sr1 = ReturnBuilder.planetary(natal, "Saturn", occurrence=1).calculate() >>> >>> # Jupiter Return nearest to 2025 >>> jr = ReturnBuilder.planetary( ... natal, "Jupiter", near_date="2025-06-01" ... ).calculate()
- classmethod solar(natal, year, *, location=None)[source]¶
Create a Solar Return builder.
A Solar Return is the chart cast for when the Sun returns to its exact natal position. This happens approximately on your birthday each year (but the exact time varies).
- Parameters:
natal (
CalculatedChart) – The natal chartyear (
int) – Year to calculate the return forlocation (
str|tuple[float,float] |ChartLocation|None) – Override location (for relocated Solar Return)
- Return type:
- Returns:
ReturnBuilder configured for Solar Return
Example
>>> sr_2025 = ReturnBuilder.solar(natal, 2025).calculate()
- class stellium.SynthesisBuilder(chart1, chart2, method)[source]¶
Bases:
objectBuilder for synthesizing two charts into one (composite or davison).
Example:
# Simple davison davison = SynthesisBuilder.davison(chart1, chart2).calculate() # Configured composite composite = (SynthesisBuilder.composite(chart1, chart2) .with_midpoint_method("short_arc") .with_labels("Alice", "Bob") .calculate())
- calculate()[source]¶
Calculate the synthesis chart.
- Return type:
- Returns:
SynthesisChart (subclass of CalculatedChart)
- classmethod composite(chart1, chart2)[source]¶
Create composite chart (midpoint of all positions).
- Parameters:
chart1 (
CalculatedChart|Native) – First chart (CalculatedChart or Native)chart2 (
CalculatedChart|Native) – Second chart (CalculatedChart or Native)
- Return type:
- Returns:
SynthesisBuilder configured for composite calculation
- classmethod davison(chart1, chart2)[source]¶
Create davison chart (midpoint in time and space).
- Parameters:
chart1 (
CalculatedChart|Native) – First chart (CalculatedChart or Native)chart2 (
CalculatedChart|Native) – Second chart (CalculatedChart or Native)
- Return type:
- Returns:
SynthesisBuilder configured for davison calculation
- with_houses(houses)[source]¶
Set house calculation method for composite charts.
- Parameters:
houses (
bool|str) – True (default) - Derived ASC method (midpoint Ascendants, derive cusps) False - No houses (positions only) “place” - Reference place method (geographic midpoint + derived time)- Return type:
- Returns:
Self for chaining
Example
# No houses composite = SynthesisBuilder.composite(c1, c2).with_houses(False).calculate()
# Reference place method composite = SynthesisBuilder.composite(c1, c2).with_houses(“place”).calculate()
- with_labels(label1, label2)[source]¶
Set descriptive labels for source charts.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_location_method(method)[source]¶
Set location midpoint method for davison charts.
- Parameters:
method (
str) – “great_circle” (default) - Geodesic midpoint following Earth’s curvature “simple” - Arithmetic mean of lat/lon (faster but less accurate)- Return type:
- Returns:
Self for chaining
- class stellium.MultiChartBuilder(charts=None)[source]¶
Bases:
objectFluent builder for creating MultiChart objects.
Supports all multi-chart scenarios:
- For synastry:
mc = MultiChartBuilder.synastry(chart1, chart2).calculate()
- For transits:
mc = MultiChartBuilder.transit(natal, “2025-06-15”).calculate()
- For progressions:
mc = MultiChartBuilder.progression(natal, age=30).calculate()
- For 3-4 chart configurations:
- mc = (MultiChartBuilder.from_chart(natal, “Natal”)
.add_progression(age=30, label=”Progressed”) .add_transit(“2025-06-15”, label=”Transit”) .calculate())
- add_arc_direction(*, target_date=None, age=None, arc_type='solar_arc', rulership_system='traditional', label='Directed')[source]¶
Add a directed chart.
- Parameters:
- Return type:
- Returns:
Self for chaining
- add_chart(chart, label, relationship_to=0, relationship_type=None)[source]¶
Add a chart to the builder.
- Parameters:
chart (
CalculatedChart) – Chart to addlabel (
str) – Label for this chartrelationship_to (
int) – Which existing chart this relates to (default: 0)relationship_type (
ComparisonType|None) – Type of relationship (optional)
- Return type:
- Returns:
Self for chaining
- add_progression(*, target_date=None, age=None, progression_type='secondary', angle_method='quotidian', label='Progressed')[source]¶
Add a progressed chart.
- Parameters:
- Return type:
- Returns:
Self for chaining
- add_transit(transit_data, location=None, label='Transit')[source]¶
Add a transit chart.
- Parameters:
transit_data (
str|datetime|CalculatedChart) – Transit datetime or chartlocation (
Any) – Location (uses chart[0] location if None)label (
str) – Label for transit chart
- Return type:
- Returns:
Self for chaining
- classmethod arc_direction(natal_data, *, target_date=None, age=None, arc_type='solar_arc', rulership_system='traditional', natal_label='Natal', directed_label='Directed')[source]¶
Create an arc direction comparison (natal vs directed chart).
Arc directions move ALL points by the same angular distance.
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart datatarget_date (
str|datetime|None) – Target date for directionsarc_type (
str) – Type of arc to userulership_system (
Literal['traditional','modern']) – “traditional” or “modern”natal_label (
str) – Label for natal chartdirected_label (
str) – Label for directed chart
- Return type:
- Returns:
MultiChartBuilder configured for arc directions
- calculate()[source]¶
Execute all calculations and return the MultiChart.
- Return type:
- Returns:
MultiChart object with all calculated data
- classmethod from_chart(chart, label='Chart 1')[source]¶
Start building from a single chart.
Use .add_chart(), .add_transit(), etc. to add more charts.
- Parameters:
chart (
CalculatedChart) – Initial chartlabel (
str) – Label for this chart
- Return type:
- Returns:
MultiChartBuilder ready for adding more charts
- classmethod from_charts(charts, labels=None)[source]¶
Create a MultiChartBuilder from a list of calculated charts.
- Parameters:
charts (
list[CalculatedChart]) – List of 2-4 CalculatedChart objects
- Return type:
- Returns:
MultiChartBuilder ready for configuration
- classmethod progression(natal_data, progressed_data=None, *, target_date=None, age=None, progression_type='secondary', angle_method='quotidian', natal_label='Natal', progressed_label='Progressed')[source]¶
Create a progression comparison with auto-calculation support.
Supports three progression types: - secondary (default): 1 day = 1 year. The standard progression. - tertiary: 1 day = 1 lunar month (~27.3 days). Faster-moving. - minor: 1 lunar month = 1 year. Intermediate rate.
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart dataprogressed_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict] |None) – Optional pre-calculated progressed charttarget_date (
str|datetime|None) – Target date for progressionprogression_type (
Literal['secondary','tertiary','minor']) – “secondary” (default), “tertiary”, or “minor”angle_method (
Literal['quotidian','solar_arc','naibod']) – How to progress anglesnatal_label (
str) – Label for natal chartprogressed_label (
str) – Label for progressed chart
- Return type:
- Returns:
MultiChartBuilder configured for progressions
Examples:
# Secondary (standard, 1 day = 1 year) prog = MultiChartBuilder.progression(natal, age=30).calculate() # Tertiary (1 day = 1 lunar month) prog = MultiChartBuilder.progression( natal, age=30, progression_type="tertiary" ).calculate() # Minor (1 lunar month = 1 year) prog = MultiChartBuilder.progression( natal, age=30, progression_type="minor" ).calculate()
- classmethod synastry(data1, data2, label1='Person 1', label2='Person 2')[source]¶
Create a synastry comparison between two natal charts.
- Parameters:
data1 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – First person’s chart datadata2 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Second person’s chart datalabel1 (
str) – Label for first personlabel2 (
str) – Label for second person
- Return type:
- Returns:
MultiChartBuilder configured for synastry
- classmethod transit(natal_data, transit_data, natal_label='Natal', transit_label='Transit')[source]¶
Create a transit comparison (natal chart vs current sky).
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart data (CalculatedChart, Native, or tuple)transit_data (
CalculatedChart|Native|datetime|str|tuple[str|datetime|dict,str|tuple[float,float] |dict|None]) – Transit time - can be: - CalculatedChart: use as-is - Native: build chart from Native - datetime or str: use natal chart’s location - tuple[datetime, location]: build chart from tuplenatal_label (
str) – Label for natal charttransit_label (
str) – Label for transit chart
- Return type:
- Returns:
MultiChartBuilder configured for transits
Example
# Using a raw datetime (uses natal location) mc = MultiChartBuilder.transit(natal, datetime(2025, 1, 1, 12, 0))
# Using a tuple with explicit location mc = MultiChartBuilder.transit(natal, (datetime(2025, 1, 1), “New York”))
- with_aspect_engine(engine)[source]¶
Set the aspect engine for cross-chart aspects.
- Parameters:
engine – AspectEngine instance
- Return type:
- Returns:
Self for chaining
- with_cross_aspects(pairs='to_primary')[source]¶
Configure which chart pairs to calculate cross-aspects for.
- Parameters:
pairs (
Union[list[tuple[int,int]],Literal['all','to_primary','adjacent']]) – Either: - “to_primary”: Only aspects to chart[0] (default) - “adjacent”: Adjacent pairs (0-1, 1-2, 2-3) - “all”: All possible pairs - List of (i, j) tuples for explicit pairs- Return type:
- Returns:
Self for chaining
- with_house_overlays(enabled=True)[source]¶
Enable or disable house overlay calculation.
- Parameters:
enabled (
bool) – Whether to calculate house overlays- Return type:
- Returns:
Self for chaining
- with_internal_aspect_engine(engine)[source]¶
Set aspect engine for calculating internal (natal) aspects.
- Parameters:
engine – AspectEngine instance
- Return type:
- Returns:
Self for chaining
- with_internal_orb_engine(engine)[source]¶
Set orb engine for calculating internal (natal) aspects.
- Parameters:
engine (
OrbEngine) – OrbEngine instance- Return type:
- Returns:
Self for chaining
- with_labels(labels)[source]¶
Set labels for each chart.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_orb_engine(engine)[source]¶
Set the orb engine for cross-chart aspects.
- Parameters:
engine (
OrbEngine) – OrbEngine instance- Return type:
- Returns:
Self for chaining
- without_cross_aspects()[source]¶
Disable cross-aspect calculation.
- Return type:
- Returns:
Self for chaining
- class stellium.ReportBuilder[source]¶
Bases:
objectBuilder for chart reports.
Example:
report = ( ReportBuilder() .from_chart(chart) .with_chart_overview() .with_planet_positions() .render(format="rich_table") )
- from_chart(chart)[source]¶
Set the chart to generate reports from.
- Parameters:
chart (
CalculatedChart|Comparison|MultiChart) – A CalculatedChart, Comparison, or MultiChart- Return type:
- Returns:
Self for chaining
- preset_aspects_only()[source]¶
Aspects-only preset: Focus on planetary relationships.
Includes: - Chart overview - All aspects (with orbs) - Aspect patterns (Grand Trines, T-Squares, etc.)
- Return type:
- Returns:
Self for chaining
Note: Aspect patterns require AspectPatternAnalyzer component.
Example
>>> report = ReportBuilder().from_chart(chart).preset_aspects_only().render()
- preset_detailed()[source]¶
Detailed preset: Comprehensive report with all major sections.
Includes: - Chart overview - Moon phase - Planet positions (with speed and all house systems) - Declinations - All aspects (sorted by orb) - House cusps - Essential dignities
- Return type:
- Returns:
Self for chaining
Example
>>> report = ReportBuilder().from_chart(chart).preset_detailed().render()
- preset_full()[source]¶
Full preset: Everything available.
Includes all sections for maximum detail: - Chart overview - Moon phase - Planet positions (with speed and all house systems) - Declinations - All aspects - Aspect patterns (Grand Trines, T-Squares, etc.) - House cusps - Essential dignities - Midpoints and midpoint aspects - Fixed stars - Zodiacal Releasing (Part of Fortune and Part of Spirit)
Note: Some sections require specific components to be added during chart calculation (e.g., DignityComponent, AspectPatternAnalyzer, MidpointCalculator, FixedStarsComponent, ZodiacalReleasingAnalyzer). Missing components show helpful messages rather than errors.
- Return type:
- Returns:
Self for chaining
Example
>>> chart = (ChartBuilder.from_native(native) ... .with_aspects() ... .add_component(DignityComponent()) ... .add_analyzer(AspectPatternAnalyzer()) ... .add_component(MidpointCalculator()) ... .add_component(FixedStarsComponent()) ... .add_analyzer(ZodiacalReleasingAnalyzer(["Part of Fortune", "Part of Spirit"])) ... .calculate()) >>> report = ReportBuilder().from_chart(chart).preset_full().render()
- preset_minimal()[source]¶
Minimal preset: Just the basics.
Includes: - Chart overview (name, date, location) - Planet positions
- Return type:
- Returns:
Self for chaining
Example
>>> report = ReportBuilder().from_chart(chart).preset_minimal().render()
- preset_positions_only()[source]¶
Positions-only preset: Focus on planetary placements.
Includes: - Chart overview - Planet positions (with speed and house placements) - Declinations - House cusps
No aspects or interpretive sections.
- Return type:
- Returns:
Self for chaining
Example
>>> report = ReportBuilder().from_chart(chart).preset_positions_only().render()
- preset_standard()[source]¶
Standard preset: Common report sections for everyday use.
Includes: - Chart overview - Planet positions (with house placements) - Major aspects (sorted by orb) - House cusps
- Return type:
- Returns:
Self for chaining
Example
>>> report = ReportBuilder().from_chart(chart).preset_standard().render()
- preset_synastry()[source]¶
Synastry preset: Optimized for relationship comparison charts.
Designed for Comparison objects, this preset shows: - Chart overview (displays both charts’ info) - Planet positions (side-by-side tables for each chart) - Cross-chart aspects (with chart labels) - House cusps (side-by-side tables for each chart)
- Return type:
- Returns:
Self for chaining
Example
>>> comparison = ComparisonBuilder.synastry(chart1, chart2).calculate() >>> report = ReportBuilder().from_chart(comparison).preset_synastry().render()
- preset_transit()[source]¶
Transit preset: Optimized for transit comparison charts.
Shows natal chart positions alongside transit positions, with cross-chart aspects showing transiting planets’ aspects to natal positions.
Includes: - Chart overview - Planet positions (side-by-side: natal vs transit) - Cross-chart aspects (all aspects, tight orbs) - House cusps (side-by-side)
- Return type:
- Returns:
Self for chaining
Example
>>> transit = ComparisonBuilder.transit(natal, transit_time).calculate() >>> report = ReportBuilder().from_chart(transit).preset_transit().render()
- preset_transit_calendar(end, start=None, include_minor_planets=False)[source]¶
Transit calendar preset: Sky events over a date range.
Bundles all three transit calendar sections showing what’s happening in the sky between two dates. Useful for planning around retrogrades, sign changes, and eclipses.
Includes: - Planetary stations (retrograde/direct) - Sign ingresses (planets changing signs) - Eclipses (solar and lunar)
- Parameters:
- Return type:
- Returns:
Self for chaining
Example
>>> # Transit calendar for the next year from chart date >>> from datetime import timedelta >>> chart_date = chart.datetime.utc_datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .preset_transit_calendar(end=chart_date + timedelta(days=365)) ... .render()) >>> >>> # Specific date range >>> from datetime import datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .preset_transit_calendar( ... start=datetime(2025, 1, 1), ... end=datetime(2025, 12, 31) ... ) ... .render())
Note
This preset does NOT include natal chart information - it’s purely about sky events. For transits TO your natal chart, use ComparisonBuilder.transit() with preset_transit() instead.
- render(format='rich_table', file=None, show=None)[source]¶
Render the report with flexible output options.
- Parameters:
- Return type:
- Returns:
Filename if saved to file, None otherwise
- Raises:
ValueError – If no chart has been set
ValueError – If unknown format specified
Examples
# Show in terminal with Rich formatting report.render()
# Save to file (with terminal preview) report.render(format=”plain_table”, file=”chart.txt”)
# Save quietly (no terminal output) report.render(format=”plain_table”, file=”chart.txt”, show=False)
# Generate PDF with chart image and title (configured via builder) report.with_chart_image().with_title(“My Report”).render(
format=”pdf”, file=”report.pdf”
)
- with_arabic_parts(mode='all', show_formula=True, show_description=False)[source]¶
Add Arabic Parts (Lots) table.
- Parameters:
mode (
str) – Which parts to display (DEFAULT: “all”) - “all”: All calculated parts - “core”: 7 Hermetic Lots (Fortune, Spirit, Eros, etc.) - “family”: Family & Relationship Lots - “life”: Life Topic Lots - “planetary”: Planetary Exaltation Lotsshow_formula (
bool) – Include the formula column (DEFAULT: True) Formula shows as “ASC + Point2 - Point3” with * for sect-aware partsshow_description (
bool) – Include part descriptions (DEFAULT: False)
- Return type:
- Returns:
Self for chaining
Example
>>> # Show all Arabic Parts with formulas >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_arabic_parts() ... .render()) >>> >>> # Show only core Hermetic Lots with descriptions >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_arabic_parts( ... mode="core", ... show_description=True ... ) ... .render())
Note
- Requires ArabicPartsCalculator to be added to chart builder:
from stellium.components.arabic_parts import ArabicPartsCalculator
- chart = (ChartBuilder.from_native(native)
.add_component(ArabicPartsCalculator()) .calculate())
- with_aspect_patterns(pattern_types='all', sort_by='type')[source]¶
Add aspect patterns table (Grand Trines, T-Squares, Yods, etc.).
- Parameters:
pattern_types (
str|list[str]) – Which pattern types to show (DEFAULT: “all”) - “all”: Show all detected patterns - list[str]: Show specific pattern typessort_by (
str) – How to sort patterns (DEFAULT: “type”) - “type”: Group by pattern type - “element”: Group by element - “count”: Sort by number of planets
- Return type:
- Returns:
Self for chaining
Note
Requires AspectPatternAnalyzer to be added to chart builder. If missing, displays helpful message instead of erroring.
- with_aspects(mode='all', orbs=True, sort_by='orb', include_aspectarian=True, aspectarian_detailed=False, aspectarian_cell_size=None, aspectarian_theme=None)[source]¶
Add aspects table with optional aspectarian grid.
- Parameters:
mode (
str) – “all”, “major”, “minor”, or “harmonic”orbs (
bool) – Show orb columnsort_by (
str) – How to sort aspects (“orb”, “planet”, or “aspect_type”)include_aspectarian (
bool) – Include aspectarian grid SVG (default: True)aspectarian_detailed (
bool) – Show orb and A/S in aspectarian cells (default: False)aspectarian_cell_size (
int|None) – Override cell size for aspectarian (default: config default)aspectarian_theme (
str|None) – Theme for aspectarian rendering (default: None)
- Return type:
- Returns:
Self for chaining
Note
The aspectarian SVG is displayed in HTML/PDF output. Terminal output shows a placeholder with dimensions.
- with_chart_image(path=None)[source]¶
Include a chart wheel image in the report.
When called without arguments, automatically generates a chart SVG using the chart’s default draw settings.
- Parameters:
path (
str|None) – Optional path to an existing SVG file. If not provided, a chart image will be auto-generated when rendering.- Return type:
- Returns:
Self for chaining
Examples
# Auto-generate chart image report.with_chart_image()
# Use existing SVG file report.with_chart_image(“my_chart.svg”)
- with_chart_overview()[source]¶
Add chart overview section (birth data, chart type, etc.).
- Return type:
- Returns:
Self for chaining
- with_cross_aspects(mode='all', orbs=True, sort_by='orb')[source]¶
Add cross-chart aspects table (for Comparison charts).
Shows aspects between chart1 planets and chart2 planets with appropriate labels for each chart.
- Parameters:
- Return type:
- Returns:
Self for chaining
Note
This section requires a Comparison object (from ComparisonBuilder). If used with a single CalculatedChart, displays a helpful message.
Example
>>> comparison = ComparisonBuilder.synastry(chart1, chart2).calculate() >>> report = (ReportBuilder() ... .from_chart(comparison) ... .with_cross_aspects(mode="major") ... .render())
- with_declination_aspects(mode='all', show_orbs=True, show_oob_status=True, sort_by='orb')[source]¶
Add declination aspects table (Parallel and Contraparallel).
Declination aspects are based on equatorial coordinates rather than ecliptic longitude. They represent a different type of planetary relationship.
Parallel: Two planets at the same declination (same hemisphere). Interpreted like a conjunction.
Contraparallel: Two planets at equal declination but opposite hemispheres. Interpreted like an opposition.
- Parameters:
mode (
str) – Which aspects to show (DEFAULT: “all”) - “all”: Both parallel and contraparallel - “parallel”: Only parallel aspects - “contraparallel”: Only contraparallel aspectsshow_orbs (
bool) – Show orb column (DEFAULT: True)show_oob_status (
bool) – Show out-of-bounds status (DEFAULT: True)sort_by (
str) – How to sort aspects (DEFAULT: “orb”) - “orb”: Tightest aspects first - “planet”: Group by planet - “aspect_type”: Group by Parallel/Contraparallel
- Return type:
- Returns:
Self for chaining
Note
- Requires .with_declination_aspects() on ChartBuilder:
- chart = (ChartBuilder.from_native(native)
.with_aspects() .with_declination_aspects(orb=1.0) .calculate())
Example
>>> report = (ReportBuilder() ... .from_chart(chart) ... .with_chart_overview() ... .with_declination_aspects(mode="all") ... .render())
- with_declinations()[source]¶
Add declinations table.
Shows planetary declinations (distance from celestial equator), direction (north/south), and out-of-bounds status.
Out-of-bounds planets have declination beyond the Sun’s maximum (~23°27’) and are considered to have extra intensity or unconventional expression.
- Return type:
- Returns:
Self for chaining
Example
>>> report = (ReportBuilder() ... .from_chart(chart) ... .with_chart_overview() ... .with_declinations() ... .render())
- with_dignities(essential='both', show_details=False)[source]¶
Add essential dignities table.
- Parameters:
- Return type:
- Returns:
Self for chaining
Note
Requires DignityComponent to be added to chart builder. If missing, displays helpful message instead of erroring.
- with_dispositors(mode='both', rulership='traditional', house_system=None, show_chains=True)[source]¶
Add dispositor analysis section.
Shows planetary and/or house-based dispositor chains, final dispositor(s), and mutual receptions.
- Parameters:
mode (
str) – Which dispositor analysis to show (DEFAULT: “both”) - “planetary”: Traditional planet-disposes-planet - “house”: Kate’s house-based innovation (life area flow) - “both”: Show both analysesrulership (
str) – “traditional” or “modern” rulership system (DEFAULT: “traditional”)house_system (
str|None) – House system for house-based mode (defaults to chart’s default)show_chains (
bool) – Whether to show full disposition chain details (DEFAULT: True)
- Return type:
- Returns:
Self for chaining
Example
>>> report = (ReportBuilder() ... .from_chart(chart) ... .with_chart_overview() ... .with_dispositors(mode="both") ... .render())
Note
- For graphical output (SVG), use the DispositorEngine directly:
from stellium.engines.dispositors import DispositorEngine, render_both_dispositors engine = DispositorEngine(chart) graph = render_both_dispositors(engine.planetary(), engine.house_based()) graph.render(“dispositors”, format=”svg”)
- with_eclipses(end, start=None, eclipse_types='both')[source]¶
Add eclipses table.
Shows solar and lunar eclipses within a date range. Useful for eclipse calendars and transit planning.
- Parameters:
- Return type:
- Returns:
Self for chaining
Example
>>> # Eclipses for the next 2 years from chart date >>> from datetime import datetime, timedelta >>> chart_date = chart.datetime.utc_datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_eclipses(end=chart_date + timedelta(days=730)) ... .render()) >>> >>> # Only solar eclipses in a specific range >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_eclipses( ... start=datetime(2025, 1, 1), ... end=datetime(2025, 12, 31), ... eclipse_types="solar" ... ) ... .render())
- with_fixed_stars(tier=None, include_keywords=True, sort_by='longitude')[source]¶
Add fixed stars table.
Shows positions and metadata for fixed stars in the chart. Requires FixedStarsComponent to be added to chart builder.
- Parameters:
tier (
int|None) – Filter to specific tier (DEFAULT: None = all tiers) - 1: Royal Stars only (Aldebaran, Regulus, Antares, Fomalhaut) - 2: Major Stars only - 3: Extended Stars only - None: All tiersinclude_keywords (
bool) – Include interpretive keywords column (DEFAULT: True)sort_by (
str) – Sort order (DEFAULT: “longitude”) - “longitude”: Zodiacal order - “magnitude”: Brightest first - “tier”: Royal first, then Major, then Extended
- Return type:
- Returns:
Self for chaining
Example
>>> # Royal stars only >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_fixed_stars(tier=1) ... .render()) >>> >>> # All stars sorted by brightness >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_fixed_stars(sort_by="magnitude") ... .render())
Note
- Requires FixedStarsComponent to be added to chart builder:
- chart = (ChartBuilder.from_native(native)
.add_component(FixedStarsComponent()) .calculate())
- with_house_cusps(systems='all')[source]¶
Add house cusps table.
- with_ingresses(end, start=None, planets=None, include_moon=False, include_minor=False)[source]¶
Add sign ingresses table.
Shows when planets enter new zodiac signs within a date range. Useful for tracking sign changes and transit planning.
- Parameters:
end (
datetime) – End date for ingress search (required)start (
datetime|None) – Start date for ingress search (optional, defaults to chart date)planets (
list[str] |None) – Which planets to include (default: Sun through Pluto)include_moon (
bool) – Include Moon ingresses (default: False, very frequent)include_minor (
bool) – Include Chiron (default: False)
- Return type:
- Returns:
Self for chaining
Example
>>> # Ingresses for the next year from chart date >>> from datetime import datetime, timedelta >>> chart_date = chart.datetime.utc_datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_ingresses(end=chart_date + timedelta(days=365)) ... .render()) >>> >>> # Specific date range with Moon included >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_ingresses( ... start=datetime(2025, 1, 1), ... end=datetime(2025, 12, 31), ... include_moon=True ... ) ... .render())
- with_midpoint_aspects(mode='conjunction', orb=1.5, midpoint_filter='all', sort_by='orb')[source]¶
Add planets aspecting midpoints table.
This shows which planets activate which midpoints - the most useful way to interpret midpoints. Typically only conjunctions matter (1-2° orb).
- Parameters:
mode (
str) – Which aspects to check (DEFAULT: “conjunction”) - “conjunction”: Only conjunctions (most common, recommended) - “hard”: Conjunction, square, opposition - “all”: All major aspectsorb (
float) – Maximum orb in degrees (DEFAULT: 1.5°) Midpoints use tighter orbs than regular aspects.midpoint_filter (
str) – Which midpoints to check (DEFAULT: “all”) - “all”: All calculated midpoints - “core”: Only Sun/Moon/ASC/MC midpointssort_by (
str) – Sort order (DEFAULT: “orb”) - “orb”: Tightest aspects first - “planet”: Group by aspecting planet - “midpoint”: Group by midpoint
- Return type:
- Returns:
Self for chaining
Example
>>> # Show planets conjunct any midpoint within 1.5° >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_midpoint_aspects() ... .render()) >>> >>> # Show hard aspects to core midpoints only >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_midpoint_aspects( ... mode="hard", ... midpoint_filter="core", ... orb=2.0 ... ) ... .render())
Note
- Requires MidpointCalculator to be added to chart builder:
- chart = (ChartBuilder.from_native(native)
.add_component(MidpointCalculator()) .calculate())
- with_midpoint_trees(tree_bases=None, branch_objects=None, orb=1.5, aspect_mode='conjunction', output='both')[source]¶
Add midpoint tree visualization section.
Generates tree diagrams showing which midpoints aspect focal points. This is a standard Uranian/Hamburg astrology technique for interpreting planetary pictures.
- Parameters:
tree_bases (
list[str] |None) – Focal points to build trees for (DEFAULT: Sun, Moon, MC, ASC)branch_objects (
list[str] |None) – Objects to include in midpoint pairs. Default: 10 planets + ASC + MC + True Nodeorb (
float) – Maximum orb in degrees (DEFAULT: 1.5°)aspect_mode (
str) – Which aspects to check (DEFAULT: “conjunction”) - “conjunction”: Only conjunctions (0°) - “hard”: Conjunction + 45° series (0°, 45°, 90°, 135°, 180°) - “all”: All major aspectsoutput (
str) – What to generate (DEFAULT: “both”) - “svg”: Just SVG visualization - “text”: Just text output - “both”: Both SVG and text
- Return type:
- Returns:
Self for chaining
Example
>>> # Show midpoint trees with hard aspects >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_midpoint_trees(aspect_mode="hard") ... .render()) >>> >>> # Custom focal points with conjunction only >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_midpoint_trees( ... tree_bases=["Sun", "Moon"], ... orb=2.0, ... aspect_mode="conjunction" ... ) ... .render())
Note
- Requires MidpointCalculator to be added to chart builder:
- chart = (ChartBuilder.from_native(native)
.add_component(MidpointCalculator()) .calculate())
- with_midpoints(mode='all', threshold=None)[source]¶
Add midpoints table.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_planet_positions(include_speed=False, include_house=True, house_systems='all')[source]¶
Add planet positions table.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_profections(age=None, date=None, include_monthly=True, include_multi_point=True, include_timeline=False, timeline_range=None, points=None, house_system=None, rulership='traditional')[source]¶
Add profection timing analysis section.
Profections are a Hellenistic technique where the ASC advances one sign per year. The planet ruling that sign becomes the “Lord of the Year.”
- Parameters:
age (
int|None) – Age for profection (either age OR date required)date (
str|None) – Target date as ISO string (e.g., “2025-06-15”)include_monthly (
bool) – Show monthly profection when date is providedinclude_multi_point (
bool) – Show lords for ASC, Sun, Moon, MCinclude_timeline (
bool) – Show timeline table of Lordstimeline_range (
tuple[int,int] |None) – Custom range for timeline (e.g., (25, 40))points (
list[str] |None) – Custom points for multi-point analysishouse_system (
str|None) – House system to use (default: prefers Whole Sign)rulership (
str) – “traditional” or “modern”
- Return type:
- Returns:
Self for chaining
Example:
# By age report = ( ReportBuilder() .from_chart(chart) .with_profections(age=30) .render() ) # By date with timeline report = ( ReportBuilder() .from_chart(chart) .with_profections(date="2025-06-15", include_timeline=True) .render() )
- with_profections_wheel(age=None, date=None, compare_ages=None, show_wheel=True, show_table=True, house_system=None, rulership='traditional')[source]¶
Add profection wheel visualization section.
Generates a visual wheel diagram showing annual profections: - Circular wheel with ages 0-95 spiraling through 12 houses - Zodiac signs and house labels around the perimeter - Natal planet positions marked on the wheel - Current age highlighted - Summary table with profection details
- Parameters:
age (
int|None) – Current age to highlight (either age OR date required)date (
str|None) – Target date as ISO string (e.g., “2025-06-15”)compare_ages (
list[int] |None) – List of ages to compare in table (default: current and next)show_wheel (
bool) – Whether to show the wheel visualization (default: True)show_table (
bool) – Whether to show the summary table (default: True)house_system (
str|None) – House system to use (default: prefers Whole Sign)rulership (
str) – “traditional” or “modern”
- Return type:
- Returns:
Self for chaining
Example:
# By age with both wheel and table report = ( ReportBuilder() .from_chart(chart) .with_profections_wheel(age=30) .render(format="pdf", file="profections.pdf") ) # Compare specific ages report = ( ReportBuilder() .from_chart(chart) .with_profections_wheel( age=30, compare_ages=[30, 31, 32] ) .render() )
- with_section(section)[source]¶
Add a custom section.
This allows users to extend the report system with their own sections.
- Parameters:
section (
ReportSection) – Any object implementing the ReportSection protocol- Return type:
- Returns:
Self for chaining
Example:
class MyCustomSection: @property def section_name(self) -> str: return "My Analysis" def generate_data(self, chart: CalculatedChart) -> dict: return {"type": "text", "text": "Custom analysis..."} report = ( ReportBuilder() .from_chart(chart) .with_section(MyCustomSection()) .render() )
- with_stations(end, start=None, planets=None, include_minor=False)[source]¶
Add planetary stations (retrograde/direct) table.
Shows when planets station retrograde or direct within a date range. Useful for retrograde calendars and transit planning.
- Parameters:
- Return type:
- Returns:
Self for chaining
Example
>>> # Stations for the next year from chart date >>> from datetime import datetime, timedelta >>> chart_date = chart.datetime.utc_datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_stations(end=chart_date + timedelta(days=365)) ... .render()) >>> >>> # Specific date range >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_stations( ... start=datetime(2025, 1, 1), ... end=datetime(2025, 12, 31) ... ) ... .render())
- with_title(title)[source]¶
Set a custom title for the report.
The title appears on the cover page of PDF reports. If not set, a default title is generated from the chart’s name.
- Parameters:
title (
str) – Custom title string- Return type:
- Returns:
Self for chaining
Examples
report.with_title(“Birth Chart Analysis”) report.with_title(“Albert Einstein - Complete Natal Analysis”)
- with_zodiacal_releasing(lots=None, mode='both', query_date=None, query_age=None, context_periods=2)[source]¶
Add Zodiacal Releasing timing analysis section.
Zodiacal Releasing is a Hellenistic predictive technique that divides life into major periods ruled by signs, showing when different life themes are activated.
- Parameters:
lots (
str|list[str] |None) – Which lot(s) to display: - str: Single lot name (e.g., “Part of Fortune”) - list[str]: Multiple lots (e.g., [“Part of Fortune”, “Part of Spirit”]) - None: All lots calculated in the chart (DEFAULT)mode (
str) – Display mode: - “snapshot”: Current periods only - “timeline”: L1 timeline only - “both”: Both snapshot and timeline (DEFAULT)query_date (
str|None) – Date for snapshot as ISO string (defaults to now)query_age (
float|None) – Age for snapshot (alternative to query_date)context_periods (
int) – Number of L3/L4 periods to show before/after current (default: 2)
- Return type:
- Returns:
Self for chaining
Note
Requires ZodiacalReleasingAnalyzer to be added during chart calculation:
from stellium.engines.releasing import ZodiacalReleasingAnalyzer
- chart = (
ChartBuilder.from_native(native) .add_analyzer(ZodiacalReleasingAnalyzer([“Part of Fortune”, “Part of Spirit”])) .calculate()
)
Example:
# Show current ZR state for all calculated lots report = ( ReportBuilder() .from_chart(chart) .with_zodiacal_releasing() .render() ) # Show ZR for specific lot at specific age report = ( ReportBuilder() .from_chart(chart) .with_zodiacal_releasing( lots="Part of Fortune", mode="snapshot", query_age=30 ) .render() ) # Show only L1 timeline for Fortune and Spirit report = ( ReportBuilder() .from_chart(chart) .with_zodiacal_releasing( lots=["Part of Fortune", "Part of Spirit"], mode="timeline" ) .render() )
- with_zr_visualization(lot='Part of Fortune', year=None, levels=(1, 2, 3), output='both')[source]¶
Add Zodiacal Releasing visualization (SVG timeline diagram).
Generates visual timeline diagrams in Honeycomb Collective style: - Overview page: natal angles chart + period length reference - Timeline page: stacked L1/L2/L3 timelines with peak shapes
- Parameters:
lot (
str) – Which lot to visualize (default: “Part of Fortune”)year (
int|None) – Year to visualize (defaults to current year)levels (
tuple[int,...]) – Which levels to show in timeline (default: 1, 2, 3)output (
str) – What to generate: - “overview”: Just the overview page - “timeline”: Just the timeline visualization - “both”: Both pages (DEFAULT)
- Return type:
- Returns:
Self for chaining
Note
Requires ZodiacalReleasingAnalyzer to be added during chart calculation:
from stellium.engines.releasing import ZodiacalReleasingAnalyzer
- chart = (
ChartBuilder.from_native(native) .add_analyzer(ZodiacalReleasingAnalyzer([“Part of Fortune”])) .calculate()
)
Example:
# Add ZR visualization to PDF report report = ( ReportBuilder() .from_chart(chart) .with_chart_overview() .with_zr_visualization(lot="Part of Fortune", year=2025) .render(format="pdf", file="report.pdf") )
- class stellium.PlannerBuilder(native)[source]¶
Bases:
objectFluent builder for creating personalized astrological planners.
Example
>>> from stellium import Native >>> from stellium.planner import PlannerBuilder >>> >>> native = Native("1990-05-15 14:30", "San Francisco, CA") >>> planner = (PlannerBuilder.for_native(native) ... .year(2025) ... .timezone("America/Los_Angeles") ... .with_natal_chart() ... .with_solar_return() ... .include_natal_transits() ... .generate("my_planner.pdf"))
- binding_margin(inches)[source]¶
Add extra margin for binding.
- Parameters:
inches (
float) – Extra margin in inches (added to inner edge)- Return type:
- Returns:
Self for chaining
- date_range(start, end)[source]¶
Set a custom date range for the planner.
- Parameters:
- Return type:
- Returns:
Self for chaining
- classmethod for_native(native)[source]¶
Start building a planner for a native.
- Parameters:
native (
Native) – The Native whose planner to create- Return type:
- Returns:
PlannerBuilder instance for chaining
- include_ingresses(planets=None)[source]¶
Include planet sign ingresses.
- include_mundane_transits(enabled=True)[source]¶
Include mundane transits (planet-to-planet in sky).
- Return type:
- include_natal_transits(planets=None)[source]¶
Include transits to natal planets.
- include_stations(planets=None)[source]¶
Include retrograde/direct stations.
- include_voc(mode='traditional')[source]¶
Include Void of Course Moon periods.
- Parameters:
mode (
Literal['traditional','modern']) – “traditional” (Sun-Saturn) or “modern” (includes outer planets)- Return type:
- Returns:
Self for chaining
- location(location)[source]¶
Set location for angle calculations and planetary hours.
Defaults to the native’s birth location if not specified.
- page_size(size)[source]¶
Set page size.
- Parameters:
size (
Literal['a4','a5','letter','half-letter']) – “a4” (default), “a5”, “letter”, or “half-letter” (alias for a5)- Return type:
- Returns:
Self for chaining
- timezone(tz)[source]¶
Set the timezone for transit times.
This is required - transit times will be displayed in this timezone.
- Parameters:
tz (
str) – Timezone string (e.g., “America/Los_Angeles”, “Europe/London”)- Return type:
- Returns:
Self for chaining
- week_starts_on(day)[source]¶
Set the first day of the week for calendar grids.
- Parameters:
day (
Literal['sunday','monday']) – “sunday” (default) or “monday”- Return type:
- Returns:
Self for chaining
- with_graphic_ephemeris(harmonic=360, enabled=True)[source]¶
Include graphic ephemeris for the planner period.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_profections(enabled=True)[source]¶
Include annual profection info (Lord of the Year).
- Return type:
- with_progressed_chart(enabled=True)[source]¶
Include secondary progressed chart in front matter.
- Return type:
- with_solar_return(enabled=True)[source]¶
Include solar return chart for the planner year.
- Return type:
- with_zr_timeline(lot='Part of Fortune', enabled=True)[source]¶
Include Zodiacal Releasing timeline visualization.
- Parameters:
- Return type:
- Returns:
Self for chaining
- class stellium.ElectionalSearch(start, end, location)[source]¶
Bases:
objectFind auspicious times matching astrological conditions.
The search engine accepts conditions (callable filters) and finds times within a date range where all conditions are met.
Conditions can be: - Lambda functions: lambda c: c.get_object(“Moon”).phase.is_waxing - Helper predicates: is_waxing(), not_voc(), on_angle(“Jupiter”) - Composed conditions: all_of(cond1, cond2), any_of(cond1, cond2), not_(cond)
Example
>>> search = ElectionalSearch("2025-01-01", "2025-06-30", "San Francisco, CA") >>> results = (search ... .where(lambda c: c.get_object("Moon").phase.is_waxing) ... .where(lambda c: c.get_object("Moon").sign not in ["Scorpio", "Capricorn"]) ... .find_windows())
- start¶
Search range start
- end¶
Search range end
- location¶
Location for chart calculations
- count(step='hour', optimize=True)[source]¶
Count how many moments match conditions (without storing them).
Uses interval algebra for O(windows) performance when optimize=True and all conditions have window generators. Falls back to iteration otherwise.
- find_moments(max_results=100, step='hour', optimize=True)[source]¶
Find specific moments meeting all conditions.
Uses hierarchical filtering for performance: day-level conditions are checked first to skip entire days that can’t have valid moments.
- Parameters:
max_results (
int) – Maximum number of results to returnstep (
Literal['minute','5min','15min','30min','hour','2hour','4hour','day']) – Time step granularity (default: “hour”) - “minute”: Every minute (slow, use for short ranges) - “5min”, “15min”, “30min”: 5/15/30 minute steps - “hour”: Every hour (good default) - “2hour”, “4hour”: Coarser steps for long ranges - “day”: Daily (for very long ranges, may miss windows)optimize (
bool) – If True, use hierarchical day-level filtering (default True)
- Return type:
list[ElectionMoment]- Returns:
List of ElectionMoment objects, sorted by datetime
- find_windows(step='hour', min_duration_minutes=0, optimize=True)[source]¶
Find time windows where all conditions are met.
Adjacent passing moments are coalesced into windows. This is useful for seeing “good periods” rather than individual moments.
Uses hierarchical filtering for performance: day-level conditions are checked first to skip entire days that can’t have valid moments.
- Parameters:
- Return type:
list[ElectionWindow]- Returns:
List of ElectionWindow objects, sorted by start time
- iter_moments(step='hour', optimize=True)[source]¶
Iterate over moments meeting conditions (memory efficient).
Unlike find_moments(), this yields results one at a time without storing them all in memory. Useful for very long searches.
Uses interval algebra for performance when optimize=True and predicates have window generators attached. Falls back to day-level filtering for predicates without window generators.
- where(condition)[source]¶
Add a condition that must be met.
Conditions are combined with AND logic. All conditions must be true for a time to be considered valid.
- Parameters:
condition (
Callable[[CalculatedChart],bool]) – A callable taking CalculatedChart, returning bool- Return type:
- Returns:
Self for method chaining
Input Data¶
- class stellium.Native(datetime_input, location_input, *, name=None, time_unknown=False)[source]¶
Bases:
objectRepresents the “native” data (time and place) for a chart. This class handles all the input parsing and data cleaning.
- datetime: ChartDateTime¶
- location: ChartLocation¶
- class stellium.Notable(name, event_type, year, month, day, hour, minute, location_input, category, subcategories=None, notable_for='', astrological_notes='', data_quality='C', sources=None, verified=False)[source]¶
Bases:
NativeA Native with curated metadata from the registry.
Represents famous births and notable events. The base Native class handles all datetime/location parsing - Notable just adds metadata.
Example
>>> notable = Notable( ... name="Albert Einstein", ... event_type="birth", ... year=1879, month=3, day=14, hour=11, minute=30, ... location_input="Ulm, Germany", ... category="scientist" ... ) >>> chart = ChartBuilder.from_native(notable).calculate()
Data Models¶
- class stellium.CalculatedChart(datetime, location, positions, house_systems=<factory>, house_placements=<factory>, aspects=(), declination_aspects=(), metadata=<factory>, zodiac_type=None, ayanamsa=None, ayanamsa_value=None, calculation_timestamp=<factory>, chart_tags=())[source]¶
Bases:
objectComplete calculated chart - the final output.
This is what a ChartBuilder returns. It’s immutable and contains everything you need to analyze or visualize the chart.
- available_components()[source]¶
List all components and analyzers whose results are available.
- Return type:
- Returns:
Sorted list of component/analyzer names that can be passed to get_component_result().
Examples:
chart.available_components() # ['Arabic Parts', 'Aspect Patterns', 'Essential Dignities']
- bazi()[source]¶
Calculate the BaZi (Four Pillars / 八字) chart for this birth data.
Uses the chart’s datetime and location to compute the Chinese Four Pillars, reusing the already-resolved timezone information.
- Returns:
A BaZiChart with all four pillars, ready for analysis.
Example:
chart = ChartBuilder.from_details("1994-01-06 11:47", "Palo Alto, CA").calculate() bazi = chart.bazi() print(bazi.hanzi) # Eight characters print(bazi.day_master) # Day Master stem print(bazi.strength()) # Strength analysis
- datetime: ChartDateTime¶
- draconic()[source]¶
Return a draconic version of this chart.
The draconic chart rotates all positions so that the North Node is at 0° Aries. This is sometimes called the “soul chart” and represents the soul’s orientation before incarnation.
The transformation subtracts the North Node’s longitude from all positions and normalizes to 0-360°.
- Return type:
- Returns:
A new CalculatedChart with all longitudes rotated and “draconic” added to chart_tags.
Example:
draconic = chart.draconic() print(draconic.get_object("Sun").sign_position) draconic.draw("draconic.svg").save()
- draw(filename='chart.svg')[source]¶
Start building a chart visualization with fluent API.
This is a convenience method that creates a ChartDrawBuilder for easy, discoverable chart visualization. It provides presets and a fluent interface for customization.
- Parameters:
filename (
str) – Output filename for the SVG- Return type:
ChartDrawBuilder- Returns:
ChartDrawBuilder instance for chaining
Example:
# Simple preset chart.draw("my_chart.svg").preset_standard().save() # Custom configuration chart.draw("custom.svg").with_theme("dark").with_moon_phase( position="top-left", show_label=True ).with_chart_info(position="top-right").save()
- draw_dial(filename='dial.svg', degrees=90)[source]¶
Draw a Uranian/Hamburg school dial chart.
Creates a dial visualization that compresses the zodiac to reveal hard aspects. On a 90° dial, conjunctions, squares, and oppositions all appear as conjunctions.
- Parameters:
- Return type:
DialDrawBuilder- Returns:
DialDrawBuilder instance for chaining
Example:
# Basic 90° dial chart.draw_dial("dial.svg").save() # With theme chart.draw_dial("dial.svg").with_theme("midnight").save() # With transits on outer ring chart.draw_dial("dial.svg") .with_outer_ring(transit_chart.get_planets(), label="Transits") .save() # 360° dial with pointer to Sun chart.draw_dial("dial.svg", degrees=360) .with_pointer(pointing_to="Sun") .save()
- draw_vedic(filename='vedic_chart.svg', style='north_indian', theme='classic', label_style='abbreviation', show_degrees=True, size=500)[source]¶
Draw a Vedic (Jyotish) chart in North Indian or South Indian style.
- Parameters:
filename (
str) – Output filename for the SVGstyle (
str) – “north_indian” (diamond) or “south_indian” (grid)theme (
str) – “classic”, “dark”, or “traditional”label_style (
str) – “abbreviation” (Ari, Su), “number” (1, 2), “glyph” (unicode symbols), or “full” (Aries, Sun)show_degrees (
bool) – Show degree + minutes for each planetsize (
int) – SVG width/height in pixels
- Return type:
- Returns:
self (for chaining)
Example:
chart.draw_vedic("north.svg", style="north_indian") chart.draw_vedic("south.svg", style="south_indian", theme="traditional")
- get_component_result(name)[source]¶
Get the result of a component or analyzer by name.
Works with any component added via .add_component() or analyzer added via .add_analyzer() on ChartBuilder.
- Parameters:
name (
str) – The component or analyzer name (e.g., “Arabic Parts”, “Essential Dignities”, “Aspect Patterns”). Also accepts the metadata key as an alias (e.g., “dignities”).- Returns:
list of CelestialPosition objects - For metadata-based components/analyzers: the stored dict or list - For dual-storage components: dict with “positions” and “metadata” keys
- Return type:
For position-based components
- Raises:
KeyError – If no component with that name was used. The error message lists all available component names.
Examples:
parts = chart.get_component_result("Arabic Parts") dignities = chart.get_component_result("Essential Dignities") patterns = chart.get_component_result("Aspect Patterns") chart.available_components()
- get_contraparallels()[source]¶
Get all contraparallel aspects (same declination, opposite hemispheres).
- get_declination_aspects(aspect_type=None)[source]¶
Get declination aspects (Parallel and Contraparallel).
- get_house(object_name, system_name=None)[source]¶
Helper method to get the house number for a specific object in a specific system.
- get_houses(system_name=None)[source]¶
Get all cusps for a specific system (or default system).
- Return type:
- get_planet_accidental(planet_name, system=None)[source]¶
Get accidental dignity for a specific planet.
- get_planet_dignity(planet_name, system='traditional')[source]¶
Get dignity calculation for a specific planet.
- get_planet_total_score(planet_name, essential_system='traditional', accidental_system=None)[source]¶
Get combined essential + accidental dignity score.
- get_strongest_planet(system='traditional')[source]¶
Find the planet with the highest dignity score (Almuten).
- house_systems: dict[str, HouseCusps]¶
- location: ChartLocation¶
- lord_of_year(age, point='ASC', house_system=None, rulership='traditional')[source]¶
Quick access to Lord of the Year.
The Lord of the Year is the planet ruling the profected sign for a given age. It’s one of the most important predictive indicators in Hellenistic astrology.
- Parameters:
- Return type:
- Returns:
Name of the ruling planet
Example:
print(chart.lord_of_year(30)) # "Saturn"
- positions: tuple[CelestialPosition, ...]¶
- profection(age=None, date=None, point='ASC', include_monthly=True, house_system=None, rulership='traditional')[source]¶
Calculate profections for this chart.
Profections move a point forward one sign per year. The planet ruling the profected sign becomes the “Lord of the Year.”
- Parameters:
age (
int|None) – Age in completed years (either age OR date required)date (
datetime|str|None) – Specific date (datetime or ISO string)point (
str) – Point to profect (default “ASC”)include_monthly (
bool) – Whether to include monthly profection (date only)house_system (
str|None) – House system to use (default: prefers Whole Sign)rulership (
str) – “traditional” or “modern” rulers
- Returns:
ProfectionResult, or tuple(annual, monthly) if include_monthly
Example:
# By age result = chart.profection(age=30) print(f"Lord of Year 30: {result.ruler}") # By date (gets both annual and monthly) annual, monthly = chart.profection(date="2025-06-15") print(f"Annual: {annual.ruler}, Monthly: {monthly.ruler}")
- profection_timeline(start_age, end_age, point='ASC', house_system=None, rulership='traditional')[source]¶
Generate profections for a range of ages.
- Parameters:
- Returns:
ProfectionTimeline with all entries
Example:
timeline = chart.profection_timeline(25, 35) for entry in timeline.entries: print(f"Age {entry.units}: {entry.ruler}")
- profections(age=None, date=None, points=None, house_system=None, rulership='traditional')[source]¶
Profect multiple points at once.
- Parameters:
- Returns:
MultiProfectionResult with all profections
Example:
result = chart.profections(age=30) print(result.lords) # {"ASC": "Saturn", "Sun": "Mars", ...}
- to_dict()[source]¶
Serialize to dictionary for JSON export.
This enables web API integration, storage, etc.
- to_prompt_text(sections=None, include_extras=True)[source]¶
Export chart data as clean, human-readable text suitable for LLM prompts.
Produces a structured markdown-style summary of the chart including planetary positions, house cusps, aspects, declination aspects, and any component results (Arabic Parts, midpoints, fixed stars, dignities, antiscia, aspect patterns, etc.).
- Parameters:
sections (
set[str] |None) – Set of section names to include. WhenNone(the default), every section that has data is included. Valid names: “info”, “positions”, “angles”, “houses”, “aspects”, “declination_aspects”, “arabic_parts”, “midpoints”, “fixed_stars”, “dignities”, “antiscia”, “patterns”, “nodes”, “points”, “extras”.include_extras (
bool) – When True (default), automatically picks up data from unknown/future components that aren’t handled by a dedicated section. Set to False to only show data from known, explicitly-supported sections.
- Return type:
- Returns:
A multi-line string ready to paste into an LLM prompt.
Example:
text = chart.to_prompt_text() prompt = f"Interpret this birth chart:\n\n{text}" # Only specific sections text = chart.to_prompt_text(sections={"info", "positions", "aspects"})
- voc_moon(aspects='traditional')[source]¶
Check if the Moon is void of course.
The Moon is void of course (VOC) when it will not complete any major Ptolemaic aspect (conjunction, sextile, square, trine, opposition) before leaving its current sign. This is traditionally considered an inauspicious time for beginning new ventures.
- Parameters:
aspects (
Literal['traditional','modern']) – Planet set to consider: - “traditional”: Sun through Saturn (visible planets) - “modern”: Includes Uranus, Neptune, Pluto- Return type:
VOCMoonResult- Returns:
VOCMoonResult with void status and timing details
Example:
voc = chart.voc_moon() if voc.is_void: print(f"Moon is VOC until {voc.void_until}") print(f"Will enter {voc.next_sign}") else: print(f"Moon will {voc.next_aspect}") print(f"Aspect perfects at {voc.void_until}")
- zodiacal_releasing(lot='Part of Fortune')[source]¶
Get the zodiacal releasing full life timeline for a given lot.
- Parameters:
lot (
str) – Name of timeline’s lot (defaults to ‘Part of Fortune’)- Return type:
ZRTimeline- Returns:
Zodiacal releasing full timeline object
- class stellium.core.models.UnknownTimeChart(datetime, location, positions, house_systems=<factory>, house_placements=<factory>, aspects=(), declination_aspects=(), metadata=<factory>, zodiac_type=None, ayanamsa=None, ayanamsa_value=None, calculation_timestamp=<factory>, chart_tags=(), moon_range=None)[source]¶
Bases:
CalculatedChartA natal chart calculated without known birth time.
Inherits from CalculatedChart for compatibility with all existing code, but has some key differences: - Houses are empty (no house cusps without birth time) - Angles are not calculated (no Asc/MC/Dsc/IC) - Moon is shown as a range rather than single position - Planetary positions are calculated for noon
The chart can still be: - Visualized (without houses, with moon arc) - Exported to JSON - Used for aspect calculations (using noon Moon) - Used for dignity calculations (planets only)
- moon_range¶
MoonRange showing the Moon’s possible positions throughout the day
- get_house(object_name, system_name=None)[source]¶
Houses are not available for unknown time charts.
- Return type:
- get_houses(system_name=None)[source]¶
Houses are not available for unknown time charts.
- Return type:
- moon_range: MoonRange = None¶
- class stellium.CelestialPosition(name, object_type, longitude, latitude=0.0, distance=0.0, speed_longitude=0.0, speed_latitude=0.0, speed_distance=0.0, declination=None, right_ascension=None, phase=None)[source]¶
Bases:
objectImmutable representation of a celestial object’s position.
This is the OUTPUT of ephemeris calculations.
- property declination_direction: str¶
‘north’, ‘south’, or ‘none’.
- Type:
Direction of declination
- property is_out_of_bounds: bool¶
Planet is beyond the Sun’s maximum declination (~23°27’).
Out-of-bounds planets are considered to have extra intensity, unpredictability, or unconventional expression in their significations.
The Sun’s declination varies between approximately +23.4367° and -23.4367° (the Tropic of Cancer and Tropic of Capricorn). When a planet exceeds these bounds, it’s “out of bounds.”
Moon, Mercury, Mars, and Venus can go out of bounds. Jupiter, Saturn, and outer planets rarely or never do.
- object_type: ObjectType¶
- class stellium.core.models.MidpointPosition(name, object_type, longitude, latitude=0.0, distance=0.0, speed_longitude=0.0, speed_latitude=0.0, speed_distance=0.0, declination=None, right_ascension=None, phase=None, object1=None, object2=None, is_indirect=False)[source]¶
Bases:
CelestialPositionSpecialized position type for midpoints between two celestial objects.
A midpoint represents the halfway point between two celestial objects, either along the shorter arc (direct) or the longer arc (indirect).
- object1¶
First component object
- object2¶
Second component object
- is_indirect¶
True if this is the indirect (opposite) midpoint
Example
# Sun at 10° Aries, Moon at 20° Aries # Direct midpoint: 15° Aries # Indirect midpoint: 15° Libra (opposite)
- midpoint = MidpointPosition(
name=”Midpoint:Sun/Moon”, object_type=ObjectType.MIDPOINT, longitude=15.0, # 15° Aries object1=sun_position, object2=moon_position, is_indirect=False,
)
- object1: CelestialPosition = None¶
- object2: CelestialPosition = None¶
- class stellium.FixedStarPosition(name, object_type, longitude, latitude=0.0, distance=0.0, speed_longitude=0.0, speed_latitude=0.0, speed_distance=0.0, declination=None, right_ascension=None, phase=None, swe_name='', constellation='', bayer='', tier=2, is_royal=False, magnitude=0.0, nature='', keywords=<factory>)[source]¶
Bases:
CelestialPositionPosition of a fixed star at a specific time.
Extends CelestialPosition with fixed star-specific metadata from the registry, including traditional astrological properties like planetary nature and keywords.
Fixed stars move very slowly due to precession (~1 degree per 72 years), so their positions change slightly between charts. Swiss Ephemeris handles precession automatically based on the Julian Day.
- swe_name¶
Swiss Ephemeris lookup name
- constellation¶
Traditional constellation (e.g., “Leo”)
- bayer¶
Bayer designation (e.g., “Alpha Leonis”)
- tier¶
Star tier (1=Royal, 2=Major, 3=Extended)
- is_royal¶
Whether this is one of the four Royal Stars of Persia
- magnitude¶
Apparent visual magnitude (lower = brighter)
- nature¶
Traditional planetary nature (e.g., “Mars/Jupiter”)
- keywords¶
Interpretive keywords for the star
Example:
regulus = FixedStarPosition( name="Regulus", object_type=ObjectType.FIXED_STAR, longitude=150.12, # ~0 Virgo (moves slowly through precession) swe_name="Regulus", constellation="Leo", tier=1, is_royal=True, magnitude=1.35, nature="Mars/Jupiter", keywords=("royalty", "success", "fame"), )
- class stellium.Aspect(object1, object2, aspect_name, aspect_degree, orb, is_applying=None)[source]¶
Bases:
objectImmutable aspect between two objects.
- object1: CelestialPosition¶
- object2: CelestialPosition¶
- class stellium.core.models.AspectPattern(name, planets, aspects, element=None, quality=None)[source]¶
Bases:
objectRepresents a detected aspect pattern in a chart. (e.g., Grand Trine, T-Square, Yod, etc.)
- property focal_planet: CelestialPosition | None¶
Get the focal/apex planet for patterns that have one.
- planets: list[CelestialPosition]¶
- class stellium.HouseCusps(system, cusps)[source]¶
Bases:
objectImmutable house cusp data.
- class stellium.ChartDateTime(utc_datetime, julian_day, local_datetime=None)[source]¶
Bases:
objectImmutable datetime data for chart calculation.
- class stellium.ChartLocation(latitude, longitude, name='', timezone='')[source]¶
Bases:
objectImmutable location data for chart calculation.
- class stellium.PhaseData(phase_angle, illuminated_fraction, elongation, apparent_diameter, apparent_magnitude, geocentric_parallax=0.0, sun_longitude=None, moon_longitude=None)[source]¶
Bases:
objectPlanetary phase information.
Contains data about a celestial object’s appearance and illumination as seen from Earth. Available for Moon, planets, and some asteroids.
- phase_angle¶
Angular separation from Sun (0-360°) - 0° = conjunction (new moon) - 90° = quadrature (quarter moon) - 180° = opposition (full moon)
- illuminated_fraction¶
Fraction of disk that is illuminated (0.0-1.0) - 0.0 = completely dark (new) - 0.5 = half illuminated (quarter) - 1.0 = fully illuminated (full)
- elongation¶
Elongation of the planet
- apparent_diameter¶
Angular diameter as seen from Earth (arc seconds)
- apparent_magnitude¶
Visual brightness magnitude (lower = brighter)
- geocentric_parallax¶
Parallax angle (radians) - primarily for Moon
- property is_waxing: bool¶
Whether object is waxing (growing in illumination).
For the Moon: waxing when Moon is 0-180° ahead of Sun. Uses Sun/Moon longitudes when available (accurate). Falls back to phase_angle for backward compatibility.
- class stellium.MultiChart(charts, labels=(), relationships=<factory>, cross_aspects=<factory>, house_overlays=<factory>, calculation_timestamp=<factory>, metadata=<factory>)[source]¶
Bases:
objectUnified multi-chart container supporting 2-4 charts with analysis and visualization.
Supports all chart relationship types: - Synastry (two natal charts) - Transits (natal + transit sky) - Progressions (natal + progressed) - Arc Directions (natal + directed) - Triwheels/Quadwheels (3-4 charts)
Access charts via: - Indexed: mc[0], mc[1], mc.charts[2] - Named: mc.chart1, mc.chart2, mc.chart3, mc.chart4 - Semantic: mc.inner, mc.outer, mc.natal
- charts¶
Tuple of 2-4 CalculatedChart objects
- labels¶
Display labels for each chart
- relationships¶
Per-pair relationship types {(0,1): ComparisonType.SYNASTRY}
- cross_aspects¶
Cross-chart aspects indexed by pair {(0,1): (aspects…)}
- house_overlays¶
House overlays indexed by (planet_chart, house_chart)
- calculation_timestamp¶
When this MultiChart was created
- metadata¶
Additional metadata
- calculate_compatibility_score(pair=(0, 1), weights=None)[source]¶
Calculate a simple compatibility score based on aspects.
This is a basic implementation - users can implement their own weighting schemes.
- property chart1: CalculatedChart¶
Primary chart (innermost ring).
- property chart2: CalculatedChart¶
Second chart.
- property chart3: CalculatedChart | None¶
Third chart (if present).
- property chart4: CalculatedChart | None¶
Fourth chart (if present).
- charts: tuple[CalculatedChart, ...]¶
- property datetime¶
Delegate to chart1’s datetime for visualization compatibility.
- draw(filename='multichart.svg')[source]¶
Start building a multi-chart visualization.
- Parameters:
filename (
str) – Output filename for the SVG- Return type:
ChartDrawBuilder- Returns:
ChartDrawBuilder configured for this MultiChart
- get_all_house_overlays()[source]¶
Get all house overlays flattened into a single list.
- Return type:
- Returns:
List of all HouseOverlay objects
- get_cross_aspects(chart1_idx=0, chart2_idx=1)[source]¶
Get cross-chart aspects between two specific charts.
- get_house_overlays(planet_chart, house_chart)[source]¶
Get house overlays for a specific chart pair.
- Parameters:
- Return type:
- Returns:
Tuple of HouseOverlay objects
- get_object(name, chart=0)[source]¶
Get a celestial object by name from a specific chart.
- Parameters:
- Return type:
- Returns:
CelestialPosition or None
- get_object_aspects(object_name, chart=0)[source]¶
Get all cross-chart aspects involving a specific object from a specific chart.
- Parameters:
- Return type:
- Returns:
List of Aspect objects involving the specified object
Example
>>> venus_aspects = multichart.get_object_aspects("Venus", chart=0) >>> for asp in venus_aspects: ... print(f"Venus {asp.aspect_name} {asp.object2.name}")
- get_relationship(idx1, idx2)[source]¶
Get the relationship type between two charts.
- Parameters:
- Return type:
- Returns:
ComparisonType or None if not defined
- property inner: CalculatedChart¶
Semantic alias for innermost chart (chart1).
- property location¶
Delegate to chart1’s location for visualization compatibility.
- property natal: CalculatedChart¶
Semantic alias for the natal/base chart (chart1).
- property outer: CalculatedChart¶
Semantic alias for outermost chart.
- to_prompt_text(sections=None, include_extras=True)[source]¶
Export multi-chart data as clean, human-readable text for LLM prompts.
Shows each chart clearly labeled, followed by cross-chart aspects and house overlays.
- Parameters:
- Return type:
- Returns:
A multi-line string ready to paste into an LLM prompt.
Enums¶
- class stellium.core.models.ObjectType(value)[source]¶
Bases:
EnumType of astrological object.
- ANGLE = 'angle'¶
- ANTISCION = 'antiscion'¶
- ARABIC_PART = 'arabic_part'¶
- ASTEROID = 'asteroid'¶
- CONTRA_ANTISCION = 'contra_antiscion'¶
- FIXED_STAR = 'fixed_star'¶
- MIDPOINT = 'midpoint'¶
- NODE = 'node'¶
- PLANET = 'planet'¶
- POINT = 'point'¶
- TECHNICAL = 'technical'¶
Comparison Types¶
- class stellium.Comparison(comparison_type, chart1, chart2, cross_aspects=(), house_overlays=(), chart1_label='Native', chart2_label='Other', calculation_timestamp=<factory>)[source]¶
Bases:
objectComparison between two charts (synastry or transits).
This class mimics CalculatedChart’s interface while providing cross-chart analysis. It holds two complete charts and calculates their interactions.
- calculate_compatibility_score(weights=None)[source]¶
Calculate a simple compatibility score based on aspects.
This is a basic implementation - users can implement their own weighting schemes.
- chart1: CalculatedChart¶
- chart2: CalculatedChart¶
- property chart2_datetime: ChartDateTime¶
Secondary chart datetime.
- property chart2_houses: HouseCusps¶
Secondary chart houses.
- property chart2_location: ChartLocation¶
Secondary chart location.
- property chart2_positions: tuple[CelestialPosition, ...]¶
Secondary chart positions.
- comparison_type: ComparisonType¶
- cross_aspects: tuple[ComparisonAspect, ...] = ()¶
- property datetime: ChartDateTime¶
Primary chart datetime (chart1/native).
- draw(filename='synastry.svg')[source]¶
Start building a comparison chart visualization with fluent API.
This is a convenience method that creates a ChartDrawBuilder for easy, discoverable comparison chart visualization. It provides synastry-specific presets and a fluent interface for customization.
- Parameters:
filename (
str) – Output filename for the SVG- Return type:
ChartDrawBuilder- Returns:
ChartDrawBuilder instance for chaining
Example:
# Simple synastry preset comparison.draw("synastry.svg").preset_synastry().save() # Custom configuration comparison.draw("custom.svg").with_theme("celestial").with_moon_phase( position="top-left" ).with_chart_info(position="top-right").save()
- get_object(name, chart=1)[source]¶
Get a celestial object by name from either chart.
- Parameters:
name (
str) – Object name (e.g., “Sun”, “Moon”)from_chart – Which chart to get from
- Return type:
- Returns:
CelestialPosition or None
- get_object_aspects(object_name, chart=1)[source]¶
Get all cross-chart aspects involving a specific object.
- Parameters:
- Return type:
- Returns:
List of ComparisonAspect objects
- get_object_houses(object_name, chart=1)[source]¶
Get house overlays for a specific planet.
- Parameters:
planet_name – Planet name
planet_owner – Which chart owns the planet
- Return type:
- Returns:
List of HouseOverlay objects
- get_objects_in_house(house_number, house_owner, planet_owner='both')[source]¶
Get all planets falling in a specific house.
- Parameters:
- Return type:
- Returns:
List of HouseOverlay objects
- house_overlays: tuple[HouseOverlay, ...] = ()¶
- property houses: HouseCusps¶
Primary chart houses (chart1/native).
- property location: ChartLocation¶
Primary chart location (chart1/native).
- property positions: tuple[CelestialPosition, ...]¶
Primary chart positions (chart1/native).
- class stellium.ComparisonAspect(object1, object2, aspect_name, aspect_degree, orb, is_applying=None, chart1_to_chart2=True, in_chart1_house=None, in_chart2_house=None)[source]¶
Bases:
AspectAspect between objects from two different charts.
This extends the base Aspect model with comparison-specific metadata.
- object1: CelestialPosition¶
- object2: CelestialPosition¶
- class stellium.HouseOverlay(planet_name, planet_owner, falls_in_house, house_owner, planet_position)[source]¶
Bases:
objectRepresents one chart’s planets falling in another chart’s houses.
- planet_position: CelestialPosition¶
- class stellium.SynthesisChart(datetime, location, positions, house_systems=<factory>, house_placements=<factory>, aspects=(), declination_aspects=(), metadata=<factory>, zodiac_type=None, ayanamsa=None, ayanamsa_value=None, calculation_timestamp=<factory>, chart_tags=(), synthesis_method='', source_chart1=None, source_chart2=None, chart1_label='Chart 1', chart2_label='Chart 2', midpoint_method=None, houses_config=None, location_method=None)[source]¶
Bases:
CalculatedChartA chart synthesized from two source charts (composite or davison).
Inherits all fields from CalculatedChart: - positions: tuple[CelestialPosition, …] - aspects: tuple[Aspect, …] - house_systems: dict[str, HouseCusps] - house_placements: dict[str, dict] - datetime: ChartDateTime - location: ChartLocation - metadata: dict
And adds synthesis-specific fields.
- houses_config: bool | str | None = None¶
True (derived), False (none), or “place”
- Type:
Composite only
- source_chart1: CalculatedChart | None = None¶
The first source chart (full chart object for reference)
- source_chart2: CalculatedChart | None = None¶
The second source chart (full chart object for reference)
Profections & Time Lord Techniques¶
- class stellium.ProfectionEngine(chart, house_system=None, rulership='traditional')[source]¶
Bases:
objectGeneral-purpose profection calculator.
Profections move a point forward one sign per unit of time (year, month, day). This engine handles all the complexity of looking up houses, rulers, and finding what planets are activated.
- Parameters:
chart (
CalculatedChart) – The natal chart to profect fromhouse_system (
str|None) – House system to use (default “Whole Sign” - traditional)rulership (
Literal['traditional','modern']) – Rulership system (“traditional” or “modern”)
Example
>>> engine = ProfectionEngine(chart) >>> result = engine.annual(30) # Age 30 profection >>> print(result.ruler) # Lord of the Year
- DEFAULT_POINTS = ['ASC', 'Sun', 'Moon', 'MC']¶
- annual(age, point='ASC')[source]¶
Annual profection for a given age.
This is the most common use case: what house and lord are activated for a specific year of life?
- Parameters:
- Return type:
- Returns:
ProfectionResult for that age
Example
>>> result = engine.annual(30) >>> print(f"At age 30: {result.profected_sign} year") >>> print(f"Lord of the Year: {result.ruler}")
- for_date(date, point='ASC', include_monthly=True)[source]¶
Calculate profections for a specific date.
If include_monthly is True, returns both annual and monthly profection.
- Parameters:
- Return type:
ProfectionResult|tuple[ProfectionResult,ProfectionResult]- Returns:
ProfectionResult, or tuple of (annual, monthly) if include_monthly
Example
>>> annual, monthly = engine.for_date("2025-06-15") >>> print(f"Year: {annual.ruler}, Month: {monthly.ruler}")
- lord_of_year(age, point='ASC')[source]¶
Convenience: just get the Lord of the Year.
- Parameters:
- Return type:
- Returns:
Name of the ruling planet
Example
>>> print(engine.lord_of_year(30)) # "Saturn"
- monthly(age, month, point='ASC')[source]¶
Monthly profection within a given year.
Profects forward by (age + month) signs total.
- Parameters:
- Return type:
- Returns:
ProfectionResult for that month
Example
>>> # 4th month of age 30 year >>> result = engine.monthly(age=30, month=4) >>> print(f"Month 4: {result.profected_sign}")
- multi(age, points=None)[source]¶
Profect multiple points at once.
Useful for seeing all the lords for a given age - who rules the profected ASC, Sun, Moon, MC?
- Parameters:
- Return type:
- Returns:
MultiProfectionResult with all profections
Example
>>> results = engine.multi(30) >>> print(results.lords) # {"ASC": "Saturn", "Sun": "Mars", ...}
- profect(point, units, unit_type='year')[source]¶
Core profection operation.
Profects any point forward by N signs and returns everything you’d want to know about the result.
- Parameters:
- Return type:
- Returns:
ProfectionResult with full details
Example
>>> result = engine.profect("ASC", units=30, unit_type="year") >>> print(f"House {result.profected_house}: {result.profected_sign}")
- timeline(start_age, end_age, point='ASC')[source]¶
Generate profections for a range of ages.
Useful for seeing the sequence of lords through life, or for timeline visualizations.
- Parameters:
- Return type:
- Returns:
ProfectionTimeline with all entries
Example
>>> timeline = engine.timeline(25, 35) >>> for entry in timeline.entries: ... print(f"Age {entry.units}: {entry.ruler}")
- class stellium.ProfectionResult(source_point, source_sign, source_house, units, unit_type, profected_house, profected_sign, ruler, ruler_position, ruler_house, ruler_modern, planets_in_house=<factory>)[source]¶
Bases:
objectResult of profecting a single point.
Contains everything you’d want to know about a profection: what was profected, where it landed, who rules it, and what’s there.
- source_point¶
Name of the profected point (“ASC”, “Sun”, etc.)
- source_sign¶
The sign the point is in natally
- source_house¶
The house the point is in natally (1-12)
- units¶
How many signs forward the point moved
- unit_type¶
Type of profection (“year”, “month”, “day”)
- profected_house¶
The house that is activated (1-12)
- profected_sign¶
The sign on that house cusp
- ruler¶
Traditional ruler of the profected sign (Lord of Year/Month)
- ruler_position¶
Natal position of the ruling planet
- ruler_house¶
Which house the ruler is in natally
- ruler_modern¶
Modern ruler if different from traditional
- planets_in_house¶
List of natal planets in the profected house
- planets_in_house: tuple[CelestialPosition, ...]¶
- ruler_position: CelestialPosition | None¶
- class stellium.MultiProfectionResult(age, date, results)[source]¶
Bases:
objectProfections from multiple points for the same time period.
Useful for seeing all the lords at once - e.g., who rules the profected ASC, Sun, Moon, MC, and Fortune for age 30.
- age¶
The age for these profections
- date¶
Optional specific date (for monthly profections)
- results¶
Dictionary of ProfectionResult keyed by point name
- results: dict[str, ProfectionResult]¶
- class stellium.ProfectionTimeline(point, start_age, end_age, entries)[source]¶
Bases:
objectA range of profections over time.
Useful for seeing the sequence of lords through a span of life, or for displaying in a timeline visualization.
- point¶
The point being profected (e.g., “ASC”)
- start_age¶
First age in the timeline
- end_age¶
Last age in the timeline
- entries¶
List of ProfectionResult for each age
- entries: tuple[ProfectionResult, ...]¶
I/O Functions¶
- stellium.parse_aaf(path)[source]¶
Parse an AAF (Astrodienst Astrological Format) file into Native objects.
AAF is the export format from astro.com. Each chart record consists of two lines: #A93 (human-readable data) and #B93 (computed values).
- Parameters:
- Return type:
- Returns:
List of Native objects, one per chart in the file
- Raises:
FileNotFoundError – If the file doesn’t exist
ValueError – If the file format is invalid
Example
>>> natives = parse_aaf("my_charts.aaf") >>> len(natives) 20 >>> natives[0].name 'Kate Louie' >>> chart = ChartBuilder.from_native(natives[0]).calculate()
- stellium.parse_csv(path, mapping=None, *, delimiter=',', encoding='utf-8', skip_errors=True)[source]¶
Parse a CSV file containing birth data into Native objects.
This function supports flexible CSV formats through column mapping. If no mapping is provided, it will auto-detect columns based on common naming conventions.
- Parameters:
mapping (
CSVColumnMapping|None) – Optional column mapping configuration. If None, auto-detects columns from headers.delimiter (
str) – CSV delimiter character (default: comma)encoding (
str) – File encoding (default: utf-8)skip_errors (
bool) – If True, skip rows that fail to parse and continue. If False, raise an exception on the first error.
- Return type:
- Returns:
List of Native objects, one per valid row in the CSV
- Raises:
FileNotFoundError – If the file doesn’t exist
ValueError – If required columns are missing or skip_errors=False and a row fails to parse
Example
# Auto-detect columns >>> natives = parse_csv(“birth_data.csv”)
# Custom column mapping >>> mapping = CSVColumnMapping( … name=”Full Name”, … date=”DOB”, … time=”Birth Time”, … location=”Birth Place”, … ) >>> natives = parse_csv(“birth_data.csv”, mapping=mapping)
# With date format hint for ambiguous dates >>> mapping = CSVColumnMapping( … date=”date”, … date_format=”%d/%m/%Y”, # European format … ) >>> natives = parse_csv(“european_data.csv”, mapping=mapping)
- stellium.read_csv(path, *, name=None, datetime=None, date=None, time=None, location=None, latitude=None, longitude=None, date_format=None, time_format=None)[source]¶
Simple interface for reading CSV files with common column configurations.
This is a convenience wrapper around parse_csv() that allows specifying column names as keyword arguments.
- Parameters:
- Return type:
- Returns:
List of Native objects
Example
# Simple auto-detection >>> natives = read_csv(“data.csv”)
# Specify key columns >>> natives = read_csv( … “data.csv”, … name=”Full Name”, … date=”DOB”, … time=”Birth Time”, … location=”City”, … )
# Combined first/last name >>> natives = read_csv( … “data.csv”, … name=(“First Name”, “Last Name”), … datetime=”Birth DateTime”, … latitude=”Lat”, … longitude=”Long”, … )
- stellium.parse_dataframe(df, mapping=None, *, skip_errors=True)[source]¶
Parse a pandas DataFrame containing birth data into Native objects.
This function supports flexible DataFrame formats through column mapping. If no mapping is provided, it will auto-detect columns based on common naming conventions.
- Parameters:
df (pd.DataFrame) – pandas DataFrame with birth data
mapping (CSVColumnMapping | None) – Optional column mapping configuration. If None, auto-detects columns from DataFrame column names.
skip_errors (bool) – If True, skip rows that fail to parse and continue. If False, raise an exception on the first error.
- Return type:
list[Native]
- Returns:
List of Native objects, one per valid row in the DataFrame
- Raises:
ImportError – If pandas is not installed
ValueError – If required columns are missing or skip_errors=False and a row fails to parse
Example
>>> import pandas as pd >>> from stellium.io import parse_dataframe >>> >>> df = pd.DataFrame({ ... "name": ["Kate Louie", "Albert Einstein"], ... "date": ["1994-01-06", "1879-03-14"], ... "time": ["11:47", "11:30"], ... "latitude": [37.3861, 48.4011], ... "longitude": [-122.0839, 9.9876], ... }) >>> natives = parse_dataframe(df) >>> len(natives) 2
>>> # With custom column mapping >>> mapping = CSVColumnMapping( ... name="Full Name", ... date="DOB", ... latitude="Lat", ... longitude="Lon", ... ) >>> natives = parse_dataframe(df, mapping=mapping)
- stellium.read_dataframe(df, *, name=None, datetime=None, date=None, time=None, location=None, latitude=None, longitude=None, date_format=None, time_format=None)[source]¶
Simple interface for reading pandas DataFrames with common column configurations.
This is a convenience wrapper around parse_dataframe() that allows specifying column names as keyword arguments.
- Parameters:
df (pd.DataFrame) – pandas DataFrame with birth data
name (str | tuple[str, str] | None) – Column name for person/event name, or tuple of (first, last)
datetime (str | None) – Column name for combined datetime
date (str | None) – Column name for date
time (str | None) – Column name for time
location (str | None) – Column name for location string
latitude (str | None) – Column name for latitude
longitude (str | None) – Column name for longitude
date_format (str | None) – strptime format for dates (e.g., “%d/%m/%Y”)
time_format (str | None) – strptime format for times (e.g., “%I:%M %p”)
- Return type:
list[Native]
- Returns:
List of Native objects
Example
>>> import pandas as pd >>> from stellium.io import read_dataframe >>> >>> df = pd.DataFrame({ ... "Person": ["Kate Louie"], ... "Birthday": ["1994-01-06"], ... "Birth Time": ["11:47"], ... "Lat": [37.3861], ... "Long": [-122.0839], ... }) >>> >>> natives = read_dataframe( ... df, ... name="Person", ... date="Birthday", ... time="Birth Time", ... latitude="Lat", ... longitude="Long", ... )
- stellium.dataframe_from_natives(natives, *, include_coords=True, include_timezone=False)[source]¶
Convert a list of Native objects back to a pandas DataFrame.
This is useful for exporting processed data or for round-trip operations.
- Parameters:
natives (list[Native]) – List of Native objects to convert
include_coords (bool) – Include latitude/longitude columns (default: True)
include_timezone (bool) – Include timezone column (default: False)
- Return type:
pd.DataFrame
- Returns:
pandas DataFrame with birth data
Example
>>> from stellium.io import parse_csv, dataframe_from_natives >>> >>> natives = parse_csv("birth_data.csv") >>> df = dataframe_from_natives(natives) >>> df.to_excel("birth_data.xlsx") # Export to Excel
Registry Functions¶
- stellium.get_royal_stars()[source]¶
Get the four Royal Stars of Persia.
- Return type:
list[FixedStarInfo]- Returns:
List of the four royal stars (Aldebaran, Regulus, Antares, Fomalhaut)
Core (stellium.core)¶
Core abstractions, data models, protocols, and configuration.
Models (stellium.core.models)¶
Immutable data models for astrological calculations.
These are pure data containers - no business logic, no calculations.
They represent the OUTPUT of calculations, not the process.
- class stellium.core.models.Aspect(object1, object2, aspect_name, aspect_degree, orb, is_applying=None)[source]
Bases:
objectImmutable aspect between two objects.
- aspect_degree: int
- aspect_name: str
- property description: str
Human-readable aspect description.
- object1: CelestialPosition
- object2: CelestialPosition
- orb: float
- class stellium.core.models.AspectPattern(name, planets, aspects, element=None, quality=None)[source]
Bases:
objectRepresents a detected aspect pattern in a chart. (e.g., Grand Trine, T-Square, Yod, etc.)
- property focal_planet: CelestialPosition | None
Get the focal/apex planet for patterns that have one.
- name: str
- planets: list[CelestialPosition]
- class stellium.core.models.CalculatedChart(datetime, location, positions, house_systems=<factory>, house_placements=<factory>, aspects=(), declination_aspects=(), metadata=<factory>, zodiac_type=None, ayanamsa=None, ayanamsa_value=None, calculation_timestamp=<factory>, chart_tags=())[source]
Bases:
objectComplete calculated chart - the final output.
This is what a ChartBuilder returns. It’s immutable and contains everything you need to analyze or visualize the chart.
- available_components()[source]
List all components and analyzers whose results are available.
- Return type:
- Returns:
Sorted list of component/analyzer names that can be passed to get_component_result().
Examples:
chart.available_components() # ['Arabic Parts', 'Aspect Patterns', 'Essential Dignities']
- bazi()[source]
Calculate the BaZi (Four Pillars / 八字) chart for this birth data.
Uses the chart’s datetime and location to compute the Chinese Four Pillars, reusing the already-resolved timezone information.
- Returns:
A BaZiChart with all four pillars, ready for analysis.
Example:
chart = ChartBuilder.from_details("1994-01-06 11:47", "Palo Alto, CA").calculate() bazi = chart.bazi() print(bazi.hanzi) # Eight characters print(bazi.day_master) # Day Master stem print(bazi.strength()) # Strength analysis
- calculation_timestamp: datetime
- datetime: ChartDateTime
- property default_house_system: str
- draconic()[source]
Return a draconic version of this chart.
The draconic chart rotates all positions so that the North Node is at 0° Aries. This is sometimes called the “soul chart” and represents the soul’s orientation before incarnation.
The transformation subtracts the North Node’s longitude from all positions and normalizes to 0-360°.
- Return type:
- Returns:
A new CalculatedChart with all longitudes rotated and “draconic” added to chart_tags.
Example:
draconic = chart.draconic() print(draconic.get_object("Sun").sign_position) draconic.draw("draconic.svg").save()
- draw(filename='chart.svg')[source]
Start building a chart visualization with fluent API.
This is a convenience method that creates a ChartDrawBuilder for easy, discoverable chart visualization. It provides presets and a fluent interface for customization.
- Parameters:
filename (
str) – Output filename for the SVG- Return type:
ChartDrawBuilder- Returns:
ChartDrawBuilder instance for chaining
Example:
# Simple preset chart.draw("my_chart.svg").preset_standard().save() # Custom configuration chart.draw("custom.svg").with_theme("dark").with_moon_phase( position="top-left", show_label=True ).with_chart_info(position="top-right").save()
- draw_dial(filename='dial.svg', degrees=90)[source]
Draw a Uranian/Hamburg school dial chart.
Creates a dial visualization that compresses the zodiac to reveal hard aspects. On a 90° dial, conjunctions, squares, and oppositions all appear as conjunctions.
- Parameters:
- Return type:
DialDrawBuilder- Returns:
DialDrawBuilder instance for chaining
Example:
# Basic 90° dial chart.draw_dial("dial.svg").save() # With theme chart.draw_dial("dial.svg").with_theme("midnight").save() # With transits on outer ring chart.draw_dial("dial.svg") .with_outer_ring(transit_chart.get_planets(), label="Transits") .save() # 360° dial with pointer to Sun chart.draw_dial("dial.svg", degrees=360) .with_pointer(pointing_to="Sun") .save()
- draw_vedic(filename='vedic_chart.svg', style='north_indian', theme='classic', label_style='abbreviation', show_degrees=True, size=500)[source]
Draw a Vedic (Jyotish) chart in North Indian or South Indian style.
- Parameters:
filename (
str) – Output filename for the SVGstyle (
str) – “north_indian” (diamond) or “south_indian” (grid)theme (
str) – “classic”, “dark”, or “traditional”label_style (
str) – “abbreviation” (Ari, Su), “number” (1, 2), “glyph” (unicode symbols), or “full” (Aries, Sun)show_degrees (
bool) – Show degree + minutes for each planetsize (
int) – SVG width/height in pixels
- Return type:
- Returns:
self (for chaining)
Example:
chart.draw_vedic("north.svg", style="north_indian") chart.draw_vedic("south.svg", style="south_indian", theme="traditional")
- get_accidental_dignities(system=None)[source]
Get accidental dignity calculations.
- get_all_accidental_dignities()[source]
Get all accidental dignities (entire object).
- get_angles()[source]
Get all chart angles.
- Return type:
- get_component_result(name)[source]
Get the result of a component or analyzer by name.
Works with any component added via .add_component() or analyzer added via .add_analyzer() on ChartBuilder.
- Parameters:
name (
str) – The component or analyzer name (e.g., “Arabic Parts”, “Essential Dignities”, “Aspect Patterns”). Also accepts the metadata key as an alias (e.g., “dignities”).- Returns:
list of CelestialPosition objects - For metadata-based components/analyzers: the stored dict or list - For dual-storage components: dict with “positions” and “metadata” keys
- Return type:
For position-based components
- Raises:
KeyError – If no component with that name was used. The error message lists all available component names.
Examples:
parts = chart.get_component_result("Arabic Parts") dignities = chart.get_component_result("Essential Dignities") patterns = chart.get_component_result("Aspect Patterns") chart.available_components()
- get_contraparallels()[source]
Get all contraparallel aspects (same declination, opposite hemispheres).
- get_declination_aspects(aspect_type=None)[source]
Get declination aspects (Parallel and Contraparallel).
- get_dignities(system='traditional')[source]
Get essential dignity calculations.
- get_house(object_name, system_name=None)[source]
Helper method to get the house number for a specific object in a specific system.
- get_houses(system_name=None)[source]
Get all cusps for a specific system (or default system).
- Return type:
- get_mutual_receptions(system='traditional')[source]
Get all mutual receptions in the chart.
- get_nodes()[source]
Get all nodes (True Node, South Node, etc.).
- Return type:
- get_object(name)[source]
Get a celestial object by name.
- Return type:
- get_parallels()[source]
Get all parallel aspects (same declination, same hemisphere).
- get_planet_accidental(planet_name, system=None)[source]
Get accidental dignity for a specific planet.
- get_planet_dignity(planet_name, system='traditional')[source]
Get dignity calculation for a specific planet.
- get_planet_total_score(planet_name, essential_system='traditional', accidental_system=None)[source]
Get combined essential + accidental dignity score.
- get_planets()[source]
Get all planetary objects.
- Return type:
- get_points()[source]
Get all calculated points (Vertex, Lilith, etc.).
- Return type:
- get_strongest_planet(system='traditional')[source]
Find the planet with the highest dignity score (Almuten).
- house_systems: dict[str, HouseCusps]
- location: ChartLocation
- lord_of_year(age, point='ASC', house_system=None, rulership='traditional')[source]
Quick access to Lord of the Year.
The Lord of the Year is the planet ruling the profected sign for a given age. It’s one of the most important predictive indicators in Hellenistic astrology.
- Parameters:
- Return type:
- Returns:
Name of the ruling planet
Example:
print(chart.lord_of_year(30)) # "Saturn"
- positions: tuple[CelestialPosition, ...]
- profection(age=None, date=None, point='ASC', include_monthly=True, house_system=None, rulership='traditional')[source]
Calculate profections for this chart.
Profections move a point forward one sign per year. The planet ruling the profected sign becomes the “Lord of the Year.”
- Parameters:
age (
int|None) – Age in completed years (either age OR date required)date (
datetime|str|None) – Specific date (datetime or ISO string)point (
str) – Point to profect (default “ASC”)include_monthly (
bool) – Whether to include monthly profection (date only)house_system (
str|None) – House system to use (default: prefers Whole Sign)rulership (
str) – “traditional” or “modern” rulers
- Returns:
ProfectionResult, or tuple(annual, monthly) if include_monthly
Example:
# By age result = chart.profection(age=30) print(f"Lord of Year 30: {result.ruler}") # By date (gets both annual and monthly) annual, monthly = chart.profection(date="2025-06-15") print(f"Annual: {annual.ruler}, Monthly: {monthly.ruler}")
- profection_timeline(start_age, end_age, point='ASC', house_system=None, rulership='traditional')[source]
Generate profections for a range of ages.
- Parameters:
- Returns:
ProfectionTimeline with all entries
Example:
timeline = chart.profection_timeline(25, 35) for entry in timeline.entries: print(f"Age {entry.units}: {entry.ruler}")
- profections(age=None, date=None, points=None, house_system=None, rulership='traditional')[source]
Profect multiple points at once.
- Parameters:
- Returns:
MultiProfectionResult with all profections
Example:
result = chart.profections(age=30) print(result.lords) # {"ASC": "Saturn", "Sun": "Mars", ...}
- sect()[source]
Check which sect this chart is (day or night) (Sun above the horizon).
- to_dict()[source]
Serialize to dictionary for JSON export.
This enables web API integration, storage, etc.
- to_prompt_text(sections=None, include_extras=True)[source]
Export chart data as clean, human-readable text suitable for LLM prompts.
Produces a structured markdown-style summary of the chart including planetary positions, house cusps, aspects, declination aspects, and any component results (Arabic Parts, midpoints, fixed stars, dignities, antiscia, aspect patterns, etc.).
- Parameters:
sections (
set[str] |None) – Set of section names to include. WhenNone(the default), every section that has data is included. Valid names: “info”, “positions”, “angles”, “houses”, “aspects”, “declination_aspects”, “arabic_parts”, “midpoints”, “fixed_stars”, “dignities”, “antiscia”, “patterns”, “nodes”, “points”, “extras”.include_extras (
bool) – When True (default), automatically picks up data from unknown/future components that aren’t handled by a dedicated section. Set to False to only show data from known, explicitly-supported sections.
- Return type:
- Returns:
A multi-line string ready to paste into an LLM prompt.
Example:
text = chart.to_prompt_text() prompt = f"Interpret this birth chart:\n\n{text}" # Only specific sections text = chart.to_prompt_text(sections={"info", "positions", "aspects"})
- voc_moon(aspects='traditional')[source]
Check if the Moon is void of course.
The Moon is void of course (VOC) when it will not complete any major Ptolemaic aspect (conjunction, sextile, square, trine, opposition) before leaving its current sign. This is traditionally considered an inauspicious time for beginning new ventures.
- Parameters:
aspects (
Literal['traditional','modern']) – Planet set to consider: - “traditional”: Sun through Saturn (visible planets) - “modern”: Includes Uranus, Neptune, Pluto- Return type:
VOCMoonResult- Returns:
VOCMoonResult with void status and timing details
Example:
voc = chart.voc_moon() if voc.is_void: print(f"Moon is VOC until {voc.void_until}") print(f"Will enter {voc.next_sign}") else: print(f"Moon will {voc.next_aspect}") print(f"Aspect perfects at {voc.void_until}")
- zodiac_type: Any = None
- zodiacal_releasing(lot='Part of Fortune')[source]
Get the zodiacal releasing full life timeline for a given lot.
- Parameters:
lot (
str) – Name of timeline’s lot (defaults to ‘Part of Fortune’)- Return type:
ZRTimeline- Returns:
Zodiacal releasing full timeline object
- zr_at_age(age, lot='Part of Fortune')[source]
Get the zodiacal releasing periods for a given age.
- class stellium.core.models.CelestialPosition(name, object_type, longitude, latitude=0.0, distance=0.0, speed_longitude=0.0, speed_latitude=0.0, speed_distance=0.0, declination=None, right_ascension=None, phase=None)[source]
Bases:
objectImmutable representation of a celestial object’s position.
This is the OUTPUT of ephemeris calculations.
- property declination_direction: str
‘north’, ‘south’, or ‘none’.
- Type:
Direction of declination
- distance: float = 0.0
- property is_out_of_bounds: bool
Planet is beyond the Sun’s maximum declination (~23°27’).
Out-of-bounds planets are considered to have extra intensity, unpredictability, or unconventional expression in their significations.
The Sun’s declination varies between approximately +23.4367° and -23.4367° (the Tropic of Cancer and Tropic of Capricorn). When a planet exceeds these bounds, it’s “out of bounds.”
Moon, Mercury, Mars, and Venus can go out of bounds. Jupiter, Saturn, and outer planets rarely or never do.
- is_retrograde: bool
- latitude: float = 0.0
- longitude: float
- name: str
- object_type: ObjectType
- sign: str
- sign_degree: float
- property sign_position: str
Human-readable sign position (e.g. 15°23’ Aries)
- speed_distance: float = 0.0
- speed_latitude: float = 0.0
- speed_longitude: float = 0.0
- class stellium.core.models.ChartDateTime(utc_datetime, julian_day, local_datetime=None)[source]
Bases:
objectImmutable datetime data for chart calculation.
- julian_day: float
- utc_datetime: datetime
- class stellium.core.models.ChartLocation(latitude, longitude, name='', timezone='')[source]
Bases:
objectImmutable location data for chart calculation.
- latitude: float
- longitude: float
- name: str = ''
- timezone: str = ''
- class stellium.core.models.ComparisonAspect(object1, object2, aspect_name, aspect_degree, orb, is_applying=None, chart1_to_chart2=True, in_chart1_house=None, in_chart2_house=None)[source]
Bases:
AspectAspect between objects from two different charts.
This extends the base Aspect model with comparison-specific metadata.
- aspect_degree: int
- aspect_name: str
- chart1_to_chart2: bool = True
- property description: str
Human-readable aspect description.
- object1: CelestialPosition
- object2: CelestialPosition
- orb: float
- class stellium.core.models.ComparisonType(value)[source]
Bases:
EnumType of chart comparison.
- ARC_DIRECTION = 'arc_direction'
- PROGRESSION = 'progression'
- SYNASTRY = 'synastry'
- TRANSIT = 'transit'
- class stellium.core.models.FixedStarPosition(name, object_type, longitude, latitude=0.0, distance=0.0, speed_longitude=0.0, speed_latitude=0.0, speed_distance=0.0, declination=None, right_ascension=None, phase=None, swe_name='', constellation='', bayer='', tier=2, is_royal=False, magnitude=0.0, nature='', keywords=<factory>)[source]
Bases:
CelestialPositionPosition of a fixed star at a specific time.
Extends CelestialPosition with fixed star-specific metadata from the registry, including traditional astrological properties like planetary nature and keywords.
Fixed stars move very slowly due to precession (~1 degree per 72 years), so their positions change slightly between charts. Swiss Ephemeris handles precession automatically based on the Julian Day.
- swe_name
Swiss Ephemeris lookup name
- constellation
Traditional constellation (e.g., “Leo”)
- bayer
Bayer designation (e.g., “Alpha Leonis”)
- tier
Star tier (1=Royal, 2=Major, 3=Extended)
- is_royal
Whether this is one of the four Royal Stars of Persia
- magnitude
Apparent visual magnitude (lower = brighter)
- nature
Traditional planetary nature (e.g., “Mars/Jupiter”)
- keywords
Interpretive keywords for the star
Example:
regulus = FixedStarPosition( name="Regulus", object_type=ObjectType.FIXED_STAR, longitude=150.12, # ~0 Virgo (moves slowly through precession) swe_name="Regulus", constellation="Leo", tier=1, is_royal=True, magnitude=1.35, nature="Mars/Jupiter", keywords=("royalty", "success", "fame"), )
- bayer: str = ''
- constellation: str = ''
- is_royal: bool = False
- magnitude: float = 0.0
- nature: str = ''
- swe_name: str = ''
- tier: int = 2
- class stellium.core.models.HouseCusps(system, cusps)[source]
Bases:
objectImmutable house cusp data.
- get_description(house_number)[source]
Get human-readable cusp description for a specific house.
- Return type:
- system: str
- class stellium.core.models.HouseOverlay(planet_name, planet_owner, falls_in_house, house_owner, planet_position)[source]
Bases:
objectRepresents one chart’s planets falling in another chart’s houses.
- property description: str
- falls_in_house: int
- house_owner: Literal['chart1', 'chart2']
- planet_name: str
- planet_owner: Literal['chart1', 'chart2']
- planet_position: CelestialPosition
- class stellium.core.models.MidpointPosition(name, object_type, longitude, latitude=0.0, distance=0.0, speed_longitude=0.0, speed_latitude=0.0, speed_distance=0.0, declination=None, right_ascension=None, phase=None, object1=None, object2=None, is_indirect=False)[source]
Bases:
CelestialPositionSpecialized position type for midpoints between two celestial objects.
A midpoint represents the halfway point between two celestial objects, either along the shorter arc (direct) or the longer arc (indirect).
- object1
First component object
- object2
Second component object
- is_indirect
True if this is the indirect (opposite) midpoint
Example
# Sun at 10° Aries, Moon at 20° Aries # Direct midpoint: 15° Aries # Indirect midpoint: 15° Libra (opposite)
- midpoint = MidpointPosition(
name=”Midpoint:Sun/Moon”, object_type=ObjectType.MIDPOINT, longitude=15.0, # 15° Aries object1=sun_position, object2=moon_position, is_indirect=False,
)
- is_indirect: bool = False
- object1: CelestialPosition = None
- object2: CelestialPosition = None
- class stellium.core.models.MoonRange(start_longitude, end_longitude, noon_longitude, start_sign, end_sign, crosses_sign_boundary)[source]
Bases:
objectMoon position range for unknown birth time charts.
When birth time is unknown, the Moon’s position could be anywhere within a ~12-14° range (Moon moves about 12-14° per day). This dataclass captures the full range to display on the chart.
- start_longitude
Moon position at 00:00:00 local time
- end_longitude
Moon position at 23:59:59 local time
- noon_longitude
Moon position at 12:00:00 (displayed position)
- start_sign
Zodiac sign at start of day
- end_sign
Zodiac sign at end of day
- crosses_sign_boundary
True if Moon changes sign during the day
- property arc_size: float
Size of the Moon’s arc in degrees.
Handles wrap-around at 0°/360° Aries point.
- crosses_sign_boundary: bool
- end_longitude: float
- end_sign: str
- noon_longitude: float
- property sign_display: str
Human-readable sign display for the Moon range.
- start_longitude: float
- start_sign: str
- class stellium.core.models.ObjectType(value)[source]
Bases:
EnumType of astrological object.
- ANGLE = 'angle'
- ANTISCION = 'antiscion'
- ARABIC_PART = 'arabic_part'
- ASTEROID = 'asteroid'
- CONTRA_ANTISCION = 'contra_antiscion'
- FIXED_STAR = 'fixed_star'
- MIDPOINT = 'midpoint'
- NODE = 'node'
- PLANET = 'planet'
- POINT = 'point'
- TECHNICAL = 'technical'
- class stellium.core.models.PhaseData(phase_angle, illuminated_fraction, elongation, apparent_diameter, apparent_magnitude, geocentric_parallax=0.0, sun_longitude=None, moon_longitude=None)[source]
Bases:
objectPlanetary phase information.
Contains data about a celestial object’s appearance and illumination as seen from Earth. Available for Moon, planets, and some asteroids.
- phase_angle
Angular separation from Sun (0-360°) - 0° = conjunction (new moon) - 90° = quadrature (quarter moon) - 180° = opposition (full moon)
- illuminated_fraction
Fraction of disk that is illuminated (0.0-1.0) - 0.0 = completely dark (new) - 0.5 = half illuminated (quarter) - 1.0 = fully illuminated (full)
- elongation
Elongation of the planet
- apparent_diameter
Angular diameter as seen from Earth (arc seconds)
- apparent_magnitude
Visual brightness magnitude (lower = brighter)
- geocentric_parallax
Parallax angle (radians) - primarily for Moon
- apparent_diameter: float
- apparent_magnitude: float
- elongation: float
- geocentric_parallax: float = 0.0
- illuminated_fraction: float
- property is_waxing: bool
Whether object is waxing (growing in illumination).
For the Moon: waxing when Moon is 0-180° ahead of Sun. Uses Sun/Moon longitudes when available (accurate). Falls back to phase_angle for backward compatibility.
- phase_angle: float
- property phase_name: str
Human-readable phase name (primarily for Moon).
- Returns:
Phase name like “New”, “Waxing Crescent”, etc.
- class stellium.core.models.UnknownTimeChart(datetime, location, positions, house_systems=<factory>, house_placements=<factory>, aspects=(), declination_aspects=(), metadata=<factory>, zodiac_type=None, ayanamsa=None, ayanamsa_value=None, calculation_timestamp=<factory>, chart_tags=(), moon_range=None)[source]
Bases:
CalculatedChartA natal chart calculated without known birth time.
Inherits from CalculatedChart for compatibility with all existing code, but has some key differences: - Houses are empty (no house cusps without birth time) - Angles are not calculated (no Asc/MC/Dsc/IC) - Moon is shown as a range rather than single position - Planetary positions are calculated for noon
The chart can still be: - Visualized (without houses, with moon arc) - Exported to JSON - Used for aspect calculations (using noon Moon) - Used for dignity calculations (planets only)
- moon_range
MoonRange showing the Moon’s possible positions throughout the day
- get_angles()[source]
Angles are not available for unknown time charts.
- Return type:
- get_house(object_name, system_name=None)[source]
Houses are not available for unknown time charts.
- Return type:
- get_houses(system_name=None)[source]
Houses are not available for unknown time charts.
- Return type:
- property is_time_unknown: bool
Always True for this chart type.
- moon_range: MoonRange = None
- class stellium.core.models.ZRPeriod(level, sign, ruler, start, end, length_days, is_angular, angle_from_lot, is_loosing_bond, is_peak, ruler_role=None, tenant_roles=<factory>, score=0)[source]
Bases:
objectA single Zodiacal Releasing period at any level.
Represents a time period during which a particular sign is activated in the Zodiacal Releasing technique.
- level
The period level (1=major, 2=sub, 3=sub-sub, 4=sub-sub-sub)
- sign
The zodiac sign activated during this period
- ruler
The traditional ruler of the sign
- start
When this period begins
- end
When this period ends
- length_days
Duration in days
- is_angular
Whether this sign is angular to the Lot (1st, 4th, 7th, 10th)
- angle_from_lot
The angular position (1, 4, 7, or 10) or None
- is_loosing_bond
Whether this period triggers Loosing of the Bond (L2+)
- is_peak
Whether this is a peak period (10th from Lot)
- end: datetime
- is_angular: bool
- is_loosing_bond: bool
- is_peak: bool
- length_days: float
- level: int
- ruler: str
- score: int = 0
- property sentiment: str
- sign: str
- start: datetime
- class stellium.core.models.ZRSnapshot(lot, lot_sign, date, age, l1, l2, l3, l4)[source]
Bases:
objectComplete Zodiacal Releasing state at a moment in time.
Captures which periods are active at all levels for a specific date.
- lot
Name of the lot (e.g., “Part of Fortune”)
- lot_sign
The sign the lot is placed in
- date
The queried date
- age
Age in years at this date
- l1
The active Level 1 (major) period
- l2
The active Level 2 (sub) period
- l3
The active Level 3 period (if calculated)
- l4
The active Level 4 period (if calculated)
- age: float
- date: datetime
- property is_lb: bool
Is Loosing of the Bond active at any level?
- property is_peak: bool
Are we in a 10th-from-Lot period at any level?
- l1: ZRPeriod
- l2: ZRPeriod
- l3: ZRPeriod | None
- l4: ZRPeriod | None
- lot: str
- lot_sign: str
- class stellium.core.models.ZRTimeline(lot, lot_sign, birth_date, periods, max_level)[source]
Bases:
objectComplete Zodiacal Releasing timeline for a life.
Contains all calculated periods at all levels and provides methods to query the timeline at specific dates or ages.
- lot
Name of the lot (e.g., “Part of Fortune”)
- lot_sign
The sign the lot is placed in
- birth_date
The native’s birth date
- periods
Dict mapping level (1-4) to list of periods
- max_level
Maximum level calculated (1-4)
- at_age(age)[source]
Get complete ZR state at a specific age.
- Return type:
ZRSnapshot
- at_date(date)[source]
Get complete ZR state at a specific date.
- Return type:
ZRSnapshot
- birth_date: datetime
- find_loosing_bonds(level=2)[source]
Find all Loosing of the Bond periods at a given level.
- Return type:
list[ZRPeriod]
- find_peaks(level=1)[source]
Find all peak periods (10th from Lot) at a given level.
- Return type:
list[ZRPeriod]
- lot: str
- lot_sign: str
- max_level: int
Native & Notable (stellium.core.native)¶
The Native class represents the core data for a single person or event.
Its job is to handle messy inputs (strings, dicts, naive datetimes, etc.) and process them into the clean, immutable ChartDateTime and ChartLocation objects that the rest of the system requires.
- class stellium.core.native.Native(datetime_input, location_input, *, name=None, time_unknown=False)[source]
Bases:
objectRepresents the “native” data (time and place) for a chart. This class handles all the input parsing and data cleaning.
- datetime: ChartDateTime
- location: ChartLocation
- time_unknown: bool
- class stellium.core.native.Notable(name, event_type, year, month, day, hour, minute, location_input, category, subcategories=None, notable_for='', astrological_notes='', data_quality='C', sources=None, verified=False)[source]
Bases:
NativeA Native with curated metadata from the registry.
Represents famous births and notable events. The base Native class handles all datetime/location parsing - Notable just adds metadata.
Example
>>> notable = Notable( ... name="Albert Einstein", ... event_type="birth", ... year=1879, month=3, day=14, hour=11, minute=30, ... location_input="Ulm, Germany", ... category="scientist" ... ) >>> chart = ChartBuilder.from_native(notable).calculate()
- astrological_notes: str
- category: str
- data_quality: str
- event_type: str
- property is_birth: bool
Check if this is a birth record.
- property is_event: bool
Check if this is an event record.
- name: str
- notable_for: str
- verified: bool
Registry (stellium.core.registry)¶
Celestial Objects Registry
A comprehensive catalog of all celestial objects used in astrological calculations. This centralizes metadata like names, glyphs, types, and descriptions for planets, asteroids, nodes, points, fixed stars, and other celestial bodies.
- class stellium.core.registry.AspectInfo(name, angle, category, family=None, glyph='', color='#CCCCCC', default_orb=2.0, aliases=<factory>, description='', metadata=<factory>)[source]
Bases:
objectComplete metadata for an astrological aspect.
This represents everything we know about an aspect - from its technical name and exact angle to its glyph, color, and visualization metadata.
- angle: float
- category: str
- color: str = '#CCCCCC'
- default_orb: float = 2.0
- description: str = ''
- glyph: str = ''
- name: str
- class stellium.core.registry.CelestialObjectInfo(name, display_name, object_type, glyph, glyph_svg_path=None, swiss_ephemeris_id=None, avg_daily_motion=None, category=None, aliases=<factory>, description='', metadata=<factory>)[source]
Bases:
objectComplete metadata for a celestial object.
This represents everything we know about an object - from its technical name and ephemeris ID to its glyph and human-readable description.
- description: str = ''
- display_name: str
- glyph: str
- name: str
- object_type: ObjectType
- class stellium.core.registry.FixedStarInfo(name, swe_name, glyph='★', glyph_svg_path=None, constellation='', bayer='', tier=2, is_royal=False, magnitude=0.0, nature='', keywords=<factory>, description='')[source]
Bases:
objectComplete metadata for a fixed star.
This contains everything needed to look up and interpret a fixed star, from its Swiss Ephemeris name to its traditional astrological meanings.
- bayer: str = ''
- constellation: str = ''
- description: str = ''
- glyph: str = '★'
- is_royal: bool = False
- magnitude: float = 0.0
- name: str
- nature: str = ''
- swe_name: str
- tier: int = 2
- stellium.core.registry.get_all_by_category(category)[source]
Get all celestial objects in a specific category.
- stellium.core.registry.get_all_by_type(object_type)[source]
Get all celestial objects of a specific type.
- Parameters:
object_type (
ObjectType) – The ObjectType to filter by- Return type:
list[CelestialObjectInfo]- Returns:
List of CelestialObjectInfo matching the type
- stellium.core.registry.get_all_fixed_stars()[source]
Get all fixed stars in the registry.
- Return type:
list[FixedStarInfo]- Returns:
List of all FixedStarInfo objects
- stellium.core.registry.get_aspect_by_alias(alias)[source]
Get aspect information by alias.
- stellium.core.registry.get_aspect_info(name)[source]
Get aspect information by name.
- stellium.core.registry.get_aspects_by_category(category)[source]
Get all aspects in a specific category.
- stellium.core.registry.get_aspects_by_family(family)[source]
Get all aspects in a specific family.
- stellium.core.registry.get_by_alias(alias)[source]
Get celestial object info by any of its aliases.
- stellium.core.registry.get_fixed_star_info(name)[source]
Get fixed star info by name.
- stellium.core.registry.get_object_info(name)[source]
Get celestial object info by name.
- stellium.core.registry.get_royal_stars()[source]
Get the four Royal Stars of Persia.
- Return type:
list[FixedStarInfo]- Returns:
List of the four royal stars (Aldebaran, Regulus, Antares, Fomalhaut)
- stellium.core.registry.get_stars_by_tier(tier)[source]
Get all fixed stars of a specific tier.
- stellium.core.registry.search_aspects(query)[source]
Search for aspects by name, alias, or description.
- stellium.core.registry.search_fixed_stars(query)[source]
Search for fixed stars by name, constellation, or keywords.
Comparison (stellium.core.comparison)¶
Comparison chart implementation for synastry, transits, and progressions.
This module provides a unified interface for comparing two charts: - Synastry: Two natal charts (relationship analysis) - Transits: Natal chart + current sky positions (timing analysis) - Progressions: Progressed chart + natal chart (symbolic timing)
The Comparison class mimics CalculatedChart’s interface while providing cross-chart analysis capabilities.
Configuration:¶
Uses AspectEngine + OrbEngine for aspect calculations:
AspectEngine: - Determines which aspects to calculate (via AspectConfig) - CrossChartAspectEngine for cross-chart aspects (chart1 × chart2) - ModernAspectEngine for internal aspects (if charts lack them)
OrbEngine: - Determines orb allowances for each aspect - Defaults are comparison-type specific:
Synastry: 6°/4° (moderate - connections matter)
Transits: 3°/2° (tight - timing precision)
Progressions: 1° (very tight - symbolic timing)
Builder Methods:¶
Cross-chart aspects: - .with_aspect_engine(engine) - Custom CrossChartAspectEngine - .with_orb_engine(engine) - Custom orb allowances
Internal (natal) aspects: - .with_internal_aspect_engine(engine) - Engine for chart1/chart2 internal aspects - .with_internal_orb_engine(engine) - Orbs for internal aspects
House overlays: - .without_house_overlays() - Disable house overlay calculation
- class stellium.core.comparison.Comparison(comparison_type, chart1, chart2, cross_aspects=(), house_overlays=(), chart1_label='Native', chart2_label='Other', calculation_timestamp=<factory>)[source]
Bases:
objectComparison between two charts (synastry or transits).
This class mimics CalculatedChart’s interface while providing cross-chart analysis. It holds two complete charts and calculates their interactions.
- calculate_compatibility_score(weights=None)[source]
Calculate a simple compatibility score based on aspects.
This is a basic implementation - users can implement their own weighting schemes.
- calculation_timestamp: datetime
- chart1: CalculatedChart
- chart1_label: str = 'Native'
- chart2: CalculatedChart
- property chart2_datetime: ChartDateTime
Secondary chart datetime.
- property chart2_houses: HouseCusps
Secondary chart houses.
- chart2_label: str = 'Other'
- property chart2_location: ChartLocation
Secondary chart location.
- property chart2_positions: tuple[CelestialPosition, ...]
Secondary chart positions.
- comparison_type: ComparisonType
- cross_aspects: tuple[ComparisonAspect, ...] = ()
- property datetime: ChartDateTime
Primary chart datetime (chart1/native).
- draw(filename='synastry.svg')[source]
Start building a comparison chart visualization with fluent API.
This is a convenience method that creates a ChartDrawBuilder for easy, discoverable comparison chart visualization. It provides synastry-specific presets and a fluent interface for customization.
- Parameters:
filename (
str) – Output filename for the SVG- Return type:
ChartDrawBuilder- Returns:
ChartDrawBuilder instance for chaining
Example:
# Simple synastry preset comparison.draw("synastry.svg").preset_synastry().save() # Custom configuration comparison.draw("custom.svg").with_theme("celestial").with_moon_phase( position="top-left" ).with_chart_info(position="top-right").save()
- get_angles(chart=1)[source]
Get all chart angles from specified chart.
- Return type:
- get_object(name, chart=1)[source]
Get a celestial object by name from either chart.
- Parameters:
name (
str) – Object name (e.g., “Sun”, “Moon”)from_chart – Which chart to get from
- Return type:
- Returns:
CelestialPosition or None
- get_object_aspects(object_name, chart=1)[source]
Get all cross-chart aspects involving a specific object.
- Parameters:
- Return type:
- Returns:
List of ComparisonAspect objects
- get_object_houses(object_name, chart=1)[source]
Get house overlays for a specific planet.
- Parameters:
planet_name – Planet name
planet_owner – Which chart owns the planet
- Return type:
- Returns:
List of HouseOverlay objects
- get_objects_in_house(house_number, house_owner, planet_owner='both')[source]
Get all planets falling in a specific house.
- Parameters:
- Return type:
- Returns:
List of HouseOverlay objects
- get_planets(chart=1)[source]
Get all planetary objects from specified chart.
- Return type:
- house_overlays: tuple[HouseOverlay, ...] = ()
- property houses: HouseCusps
Primary chart houses (chart1/native).
- property location: ChartLocation
Primary chart location (chart1/native).
- property positions: tuple[CelestialPosition, ...]
Primary chart positions (chart1/native).
- class stellium.core.comparison.ComparisonBuilder(chart1, comparison_type, chart1_label='Native')[source]
Bases:
objectFluent builder for creating Comparison objects.
Provides convenient construction methods for both synastry and transits:
- For synastry:
- comp = ComparisonBuilder.from_native(chart1)
.with_partner(chart2) .calculate()
- For transits:
- comp = ComparisonBuilder.from_native(natal_chart)
.with_transit(transit_datetime, transit_location) .calculate()
- classmethod arc_direction(natal_data, *, target_date=None, age=None, arc_type='solar_arc', rulership_system='traditional', natal_label='Natal', directed_label='Directed')[source]
Create an arc direction comparison (natal vs directed chart).
Arc directions move ALL points by the same angular distance, preserving natal relationships. This differs from progressions where each planet moves at its own rate.
- Arc types supported:
“solar_arc”: Arc = progressed Sun - natal Sun (~1°/year actual)
“naibod”: Arc = 0.9856° × years (mean solar motion)
“lunar”: Arc = progressed Moon - natal Moon (~12-13°/year)
“chart_ruler”: Arc based on planet ruling the Ascendant sign
“sect”: Day charts use solar arc, night charts use lunar arc
Any planet name (e.g., “Mars”, “Venus”): Uses that planet’s arc
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart data (Native, CalculatedChart, or tuple)target_date (
str|datetime|None) – Target date for directions (either this or age required)age (
float|None) – Age in years (alternative to target_date)arc_type (
str) – Type of arc to use (see above)rulership_system (
Literal['traditional','modern']) – “traditional” or “modern” (for chart_ruler arc)natal_label (
str) – Label for natal chart (default: “Natal”)directed_label (
str) – Label for directed chart (default: “Directed”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # Solar arc directions at age 30 >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, age=30, arc_type="solar_arc" ... ).calculate()
>>> # Naibod arc directions to a specific date >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, target_date="2025-06-15", arc_type="naibod" ... ).calculate()
>>> # Chart ruler arc (uses planet ruling ASC sign) >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, age=30, arc_type="chart_ruler" ... ).calculate()
>>> # Sect-based arc (solar for day charts, lunar for night) >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, age=30, arc_type="sect" ... ).calculate()
>>> # Mars arc directions >>> directed = ComparisonBuilder.arc_direction( ... natal_chart, age=30, arc_type="Mars" ... ).calculate()
- calculate()[source]
Execute all calculations and return the final Comparison.
This method ensures that both charts have their internal aspects calculated before computing cross-chart aspects. If a chart doesn’t already have aspects, they will be calculated using the internal aspect engine configuration.
- Return type:
- Returns:
Comparison object with all calculated data
- classmethod compare(data1, data2, comparison_type, chart1_label='Chart 1', chart2_label='Chart 2')[source]
General method for creating any type of comparison.
This is the flexible method that accepts any combination of inputs and any comparison type. Convenience methods (.synastry(), .transit(), .progression()) are thin wrappers that call this method with appropriate defaults.
- Parameters:
data1 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – First chart data (CalculatedChart, Native, or (datetime, location) tuple)data2 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Second chart data (CalculatedChart, Native, or (datetime, location) tuple)comparison_type (
str) – Type of comparison (“synastry”, “transit”, “progression”)chart1_label (
str) – Label for first chart (default: “Chart 1”)chart2_label (
str) – Label for second chart (default: “Chart 2”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # With Native objects >>> native1 = Native("1994-01-06 11:47", "Palo Alto, CA") >>> native2 = Native("2000-01-01 17:00", "Seattle, WA") >>> comparison = ComparisonBuilder.compare(native1, native2, "synastry").calculate() >>> >>> # With (datetime, location) tuples >>> comparison = ComparisonBuilder.compare( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2000-01-01 17:00", "Seattle, WA"), ... "synastry" ... ).calculate() >>> >>> # Mixed inputs >>> comparison = ComparisonBuilder.compare( ... native1, ... ("2024-11-24 14:30", None), # Uses chart1's location for transits ... "transit" ... ).calculate()
- classmethod from_native(native_chart, native_label='Native')[source]
Start building a comparison from a native chart.
Use this when you have a CalculatedChart already. Chain with .with_partner() or .with_transit()
- Parameters:
native_chart (
CalculatedChart) – The native/primary chartnative_label (
str) – Label for the native chart
- Return type:
- Returns:
ComparisonBuilder instance
- classmethod progression(natal_data, progressed_data=None, *, target_date=None, age=None, angle_method='quotidian', natal_label='Natal', progressed_label='Progressed')[source]
Create a progression comparison with auto-calculation support.
Secondary progressions use the symbolic equation “one day = one year.” To find progressed positions at age 30, look at where planets were 30 days after birth.
Can be called three ways: 1. Auto-calculate by target date: progression(natal, target_date=”2025-06-15”) 2. Auto-calculate by age: progression(natal, age=30) 3. Manual (legacy): progression(natal, progressed_chart)
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart data (Native, CalculatedChart, or (datetime, location) tuple)progressed_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict] |None) – Optional pre-calculated progressed chart (for backwards compatibility)target_date (
str|datetime|None) – Target date for progression (triggers auto-calculation)age (
float|None) – Age in years for progression (alternative to target_date)angle_method (
Literal['quotidian','solar_arc','naibod']) – How to progress angles: - “quotidian” (default): Actual daily motion from Swiss Ephemeris - “solar_arc”: Angles progress at rate of progressed Sun - “naibod”: Angles progress at mean Sun rate (59’08”/year)natal_label (
str) – Label for natal chart (default: “Natal”)progressed_label (
str) – Label for progressed chart (default: “Progressed”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # Auto-calculate by age (most convenient) >>> prog = ComparisonBuilder.progression(natal, age=30).calculate() >>> >>> # Auto-calculate by target date >>> prog = ComparisonBuilder.progression( ... natal, target_date="2025-06-15" ... ).calculate() >>> >>> # With solar arc angles >>> prog = ComparisonBuilder.progression( ... natal, age=30, angle_method="solar_arc" ... ).calculate() >>> >>> # Legacy: explicit progressed chart (backwards compatible) >>> progressed_chart = ChartBuilder.from_details( ... "1994-02-05 11:47", "Palo Alto, CA" ... ).calculate() >>> prog = ComparisonBuilder.progression(natal, progressed_chart).calculate()
- classmethod synastry(data1, data2, chart1_label='Person 1', chart2_label='Person 2')[source]
Create a synastry comparison between two natal charts.
Synastry analyzes the relationship between two people by comparing their birth charts. This is a convenience method that calls .compare() with comparison_type=”synastry”.
- Parameters:
data1 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – First person’s chart data (Native, CalculatedChart, or (datetime, location) tuple)data2 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Second person’s chart data (Native, CalculatedChart, or (datetime, location) tuple)chart1_label (
str) – Label for first person (default: “Person 1”)chart2_label (
str) – Label for second person (default: “Person 2”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # Simple string inputs >>> comparison = ComparisonBuilder.synastry( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2000-01-01 17:00", "Seattle, WA") ... ).calculate() >>> >>> # With Native objects >>> native1 = Native("1994-01-06 11:47", "Palo Alto, CA") >>> native2 = Native("2000-01-01 17:00", "Seattle, WA") >>> comparison = ComparisonBuilder.synastry(native1, native2).calculate() >>> >>> # With custom labels >>> comparison = ComparisonBuilder.synastry( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2000-01-01 17:00", "Seattle, WA"), ... chart1_label="Kate", ... chart2_label="Partner" ... ).calculate()
- classmethod transit(natal_data, transit_data, natal_label='Natal', transit_label='Transit')[source]
Create a transit comparison (natal chart vs current sky positions).
Transits analyze how current planetary positions interact with a natal chart for timing and prediction. This is a convenience method that calls .compare() with comparison_type=”transit”.
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart data (Native, CalculatedChart, or (datetime, location) tuple)transit_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict|None]) – Transit time data. Can be: - (datetime, location) tuple - (datetime, None) to use natal location - Native or CalculatedChartnatal_label (
str) – Label for natal chart (default: “Natal”)transit_label (
str) – Label for transit chart (default: “Transit”)
- Return type:
- Returns:
ComparisonBuilder instance ready to configure and calculate
Examples
>>> # Transit using natal location >>> comparison = ComparisonBuilder.transit( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2024-11-24 14:30", None) # Uses Palo Alto ... ).calculate() >>> >>> # Transit with different location >>> comparison = ComparisonBuilder.transit( ... ("1994-01-06 11:47", "Palo Alto, CA"), ... ("2024-11-24 14:30", "New York, NY") ... ).calculate() >>> >>> # With Native object >>> natal = Native("1994-01-06 11:47", "Palo Alto, CA") >>> comparison = ComparisonBuilder.transit( ... natal, ... ("2024-11-24 14:30", None) ... ).calculate()
- with_aspect_config(aspect_config)[source]
Set aspect configuration (orbs, which aspects, etc.).
- Parameters:
aspect_config (
AspectConfig) – AspectConfig instance- Return type:
- Returns:
Self for chaining
- with_aspect_engine(engine)[source]
Set the aspect engine for cross-chart aspects.
- Parameters:
engine – AspectEngine instance
- Return type:
- Returns:
Self for chaining
- with_internal_aspect_engine(engine)[source]
Set aspect engine for calculating internal (natal) aspects.
This engine will be used to calculate aspects within chart1 and chart2 if they don’t already have aspects calculated. If not set, defaults to ModernAspectEngine().
- Parameters:
engine – AspectEngine instance for internal aspects
- Return type:
- Returns:
Self for chaining
- with_internal_orb_engine(engine)[source]
Set orb engine for calculating internal (natal) aspects.
This engine will be used for orb allowances when calculating internal aspects in chart1/chart2. If not set, defaults to SimpleOrbEngine with registry defaults.
- Parameters:
engine (
OrbEngine) – OrbEngine instance for internal aspect orbs- Return type:
- Returns:
Self for chaining
- with_orb_engine(engine)[source]
Set the orb calculation engine for dynamic orb calculation.
OrbEngine will be used to calculate orbs for each planet pair dynamically (e.g., wider orbs for Sun/Moon, tighter for fast planets).
If provided, OrbEngine takes precedence over AspectConfig.orbs.
Examples
from stellium.engines.orbs import SimpleOrbEngine, LuminariesOrbEngine
# Simple engine with fixed orbs per aspect simple = SimpleOrbEngine({‘Conjunction’: 8.0, ‘Trine’: 8.0}) builder.with_orb_engine(simple)
# Luminaries engine (wider orbs for Sun/Moon) lum = LuminariesOrbEngine() builder.with_orb_engine(lum)
- Parameters:
engine – OrbEngine instance implementing get_orb_allowance()
- Return type:
- Returns:
Self for chaining
- with_other(other_input, location=None, other_label='Other', comparison_type=None)[source]
Generic method to add second chart.
This is a flexible alternative to with_partner() and with_transit().
- Parameters:
other_input (
CalculatedChart|datetime|Native) – Either a CalculatedChart, Native or datetimelocation (
ChartLocation|str|None) – Required if providing datetime. ChartLocation or str place nameother_label (
str) – Label for the other chartcomparison_type (
ComparisonType|None) – Optional comparison type (default: SYNASTRY)
- Return type:
- Returns:
Self for chaining
- with_partner(partner_chart_or_datetime_or_native, location=None, partner_label='Partner')[source]
Add partner chart for synastry comparison.
- Parameters:
partner_chart_or_datetime – Either a CalculatedChart or datetime
location (
ChartLocation|None) – Required if providing datetimepartner_label (
str) – Label for the partner chart
- Return type:
- Returns:
Self for chaining
- with_transit(transit_datetime, location=None)[source]
Add transit chart for transit comparison.
Convenience method that calls with_other() with appropriate settings.
- Parameters:
transit_datetime (
datetime) – Transit datetimelocation (
ChartLocation|None) – Optional location (defaults to native’s location)
- Return type:
- Returns:
Self for chaining
- without_house_overlays()[source]
Disable house overlay calculation.
- Return type:
- Returns:
Self for chaining
MultiChart (stellium.core.multichart)¶
Unified MultiChart implementation for 2-4 chart comparisons.
This module provides a single interface for all multi-chart scenarios: - Synastry (two natal charts) - Transits (natal + current sky) - Progressions (natal + progressed) - Arc Directions (natal + directed) - Triwheels (3 charts) - Quadwheels (4 charts)
The MultiChart class combines the features of the former Comparison and MultiWheel classes into a unified architecture.
Ring order (center -> out): - Tiny aspect center - Chart 1 ring (innermost) - primary/natal chart - Chart 2 ring - Chart 3 ring (if present) - Chart 4 ring (if present) - Zodiac ring (outermost)
- class stellium.core.multichart.MultiChart(charts, labels=(), relationships=<factory>, cross_aspects=<factory>, house_overlays=<factory>, calculation_timestamp=<factory>, metadata=<factory>)[source]
Bases:
objectUnified multi-chart container supporting 2-4 charts with analysis and visualization.
Supports all chart relationship types: - Synastry (two natal charts) - Transits (natal + transit sky) - Progressions (natal + progressed) - Arc Directions (natal + directed) - Triwheels/Quadwheels (3-4 charts)
Access charts via: - Indexed: mc[0], mc[1], mc.charts[2] - Named: mc.chart1, mc.chart2, mc.chart3, mc.chart4 - Semantic: mc.inner, mc.outer, mc.natal
- charts
Tuple of 2-4 CalculatedChart objects
- labels
Display labels for each chart
- relationships
Per-pair relationship types {(0,1): ComparisonType.SYNASTRY}
- cross_aspects
Cross-chart aspects indexed by pair {(0,1): (aspects…)}
- house_overlays
House overlays indexed by (planet_chart, house_chart)
- calculation_timestamp
When this MultiChart was created
- metadata
Additional metadata
- calculate_compatibility_score(pair=(0, 1), weights=None)[source]
Calculate a simple compatibility score based on aspects.
This is a basic implementation - users can implement their own weighting schemes.
- calculation_timestamp: datetime
- property chart1: CalculatedChart
Primary chart (innermost ring).
- property chart2: CalculatedChart
Second chart.
- property chart3: CalculatedChart | None
Third chart (if present).
- property chart4: CalculatedChart | None
Fourth chart (if present).
- property chart_count: int
Number of charts in this MultiChart.
- charts: tuple[CalculatedChart, ...]
- property datetime
Delegate to chart1’s datetime for visualization compatibility.
- draw(filename='multichart.svg')[source]
Start building a multi-chart visualization.
- Parameters:
filename (
str) – Output filename for the SVG- Return type:
ChartDrawBuilder- Returns:
ChartDrawBuilder configured for this MultiChart
- get_all_cross_aspects()[source]
Get all cross-chart aspects flattened into a single list.
- get_all_house_overlays()[source]
Get all house overlays flattened into a single list.
- Return type:
- Returns:
List of all HouseOverlay objects
- get_angles(chart=0)[source]
Get all chart angles from specified chart.
- Return type:
- get_cross_aspects(chart1_idx=0, chart2_idx=1)[source]
Get cross-chart aspects between two specific charts.
- get_house_overlays(planet_chart, house_chart)[source]
Get house overlays for a specific chart pair.
- Parameters:
- Return type:
- Returns:
Tuple of HouseOverlay objects
- get_object(name, chart=0)[source]
Get a celestial object by name from a specific chart.
- Parameters:
- Return type:
- Returns:
CelestialPosition or None
- get_object_aspects(object_name, chart=0)[source]
Get all cross-chart aspects involving a specific object from a specific chart.
- Parameters:
- Return type:
- Returns:
List of Aspect objects involving the specified object
Example
>>> venus_aspects = multichart.get_object_aspects("Venus", chart=0) >>> for asp in venus_aspects: ... print(f"Venus {asp.aspect_name} {asp.object2.name}")
- get_planets(chart=0)[source]
Get all planetary objects from specified chart.
- Return type:
- get_relationship(idx1, idx2)[source]
Get the relationship type between two charts.
- Parameters:
- Return type:
- Returns:
ComparisonType or None if not defined
- property inner: CalculatedChart
Semantic alias for innermost chart (chart1).
- property location
Delegate to chart1’s location for visualization compatibility.
- property natal: CalculatedChart
Semantic alias for the natal/base chart (chart1).
- property outer: CalculatedChart
Semantic alias for outermost chart.
- relationships: dict[tuple[int, int], ComparisonType]
- to_dict()[source]
Serialize to dictionary for JSON export.
- to_prompt_text(sections=None, include_extras=True)[source]
Export multi-chart data as clean, human-readable text for LLM prompts.
Shows each chart clearly labeled, followed by cross-chart aspects and house overlays.
- Parameters:
- Return type:
- Returns:
A multi-line string ready to paste into an LLM prompt.
- class stellium.core.multichart.MultiChartBuilder(charts=None)[source]
Bases:
objectFluent builder for creating MultiChart objects.
Supports all multi-chart scenarios:
- For synastry:
mc = MultiChartBuilder.synastry(chart1, chart2).calculate()
- For transits:
mc = MultiChartBuilder.transit(natal, “2025-06-15”).calculate()
- For progressions:
mc = MultiChartBuilder.progression(natal, age=30).calculate()
- For 3-4 chart configurations:
- mc = (MultiChartBuilder.from_chart(natal, “Natal”)
.add_progression(age=30, label=”Progressed”) .add_transit(“2025-06-15”, label=”Transit”) .calculate())
- add_arc_direction(*, target_date=None, age=None, arc_type='solar_arc', rulership_system='traditional', label='Directed')[source]
Add a directed chart.
- Parameters:
- Return type:
- Returns:
Self for chaining
- add_chart(chart, label, relationship_to=0, relationship_type=None)[source]
Add a chart to the builder.
- Parameters:
chart (
CalculatedChart) – Chart to addlabel (
str) – Label for this chartrelationship_to (
int) – Which existing chart this relates to (default: 0)relationship_type (
ComparisonType|None) – Type of relationship (optional)
- Return type:
- Returns:
Self for chaining
- add_progression(*, target_date=None, age=None, progression_type='secondary', angle_method='quotidian', label='Progressed')[source]
Add a progressed chart.
- Parameters:
- Return type:
- Returns:
Self for chaining
- add_transit(transit_data, location=None, label='Transit')[source]
Add a transit chart.
- Parameters:
transit_data (
str|datetime|CalculatedChart) – Transit datetime or chartlocation (
Any) – Location (uses chart[0] location if None)label (
str) – Label for transit chart
- Return type:
- Returns:
Self for chaining
- classmethod arc_direction(natal_data, *, target_date=None, age=None, arc_type='solar_arc', rulership_system='traditional', natal_label='Natal', directed_label='Directed')[source]
Create an arc direction comparison (natal vs directed chart).
Arc directions move ALL points by the same angular distance.
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart datatarget_date (
str|datetime|None) – Target date for directionsarc_type (
str) – Type of arc to userulership_system (
Literal['traditional','modern']) – “traditional” or “modern”natal_label (
str) – Label for natal chartdirected_label (
str) – Label for directed chart
- Return type:
- Returns:
MultiChartBuilder configured for arc directions
- calculate()[source]
Execute all calculations and return the MultiChart.
- Return type:
- Returns:
MultiChart object with all calculated data
- classmethod from_chart(chart, label='Chart 1')[source]
Start building from a single chart.
Use .add_chart(), .add_transit(), etc. to add more charts.
- Parameters:
chart (
CalculatedChart) – Initial chartlabel (
str) – Label for this chart
- Return type:
- Returns:
MultiChartBuilder ready for adding more charts
- classmethod from_charts(charts, labels=None)[source]
Create a MultiChartBuilder from a list of calculated charts.
- Parameters:
charts (
list[CalculatedChart]) – List of 2-4 CalculatedChart objects
- Return type:
- Returns:
MultiChartBuilder ready for configuration
- classmethod progression(natal_data, progressed_data=None, *, target_date=None, age=None, progression_type='secondary', angle_method='quotidian', natal_label='Natal', progressed_label='Progressed')[source]
Create a progression comparison with auto-calculation support.
Supports three progression types: - secondary (default): 1 day = 1 year. The standard progression. - tertiary: 1 day = 1 lunar month (~27.3 days). Faster-moving. - minor: 1 lunar month = 1 year. Intermediate rate.
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart dataprogressed_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict] |None) – Optional pre-calculated progressed charttarget_date (
str|datetime|None) – Target date for progressionprogression_type (
Literal['secondary','tertiary','minor']) – “secondary” (default), “tertiary”, or “minor”angle_method (
Literal['quotidian','solar_arc','naibod']) – How to progress anglesnatal_label (
str) – Label for natal chartprogressed_label (
str) – Label for progressed chart
- Return type:
- Returns:
MultiChartBuilder configured for progressions
Examples:
# Secondary (standard, 1 day = 1 year) prog = MultiChartBuilder.progression(natal, age=30).calculate() # Tertiary (1 day = 1 lunar month) prog = MultiChartBuilder.progression( natal, age=30, progression_type="tertiary" ).calculate() # Minor (1 lunar month = 1 year) prog = MultiChartBuilder.progression( natal, age=30, progression_type="minor" ).calculate()
- classmethod synastry(data1, data2, label1='Person 1', label2='Person 2')[source]
Create a synastry comparison between two natal charts.
- Parameters:
data1 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – First person’s chart datadata2 (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Second person’s chart datalabel1 (
str) – Label for first personlabel2 (
str) – Label for second person
- Return type:
- Returns:
MultiChartBuilder configured for synastry
- classmethod transit(natal_data, transit_data, natal_label='Natal', transit_label='Transit')[source]
Create a transit comparison (natal chart vs current sky).
- Parameters:
natal_data (
CalculatedChart|Native|tuple[str|datetime|dict,str|tuple[float,float] |dict]) – Natal chart data (CalculatedChart, Native, or tuple)transit_data (
CalculatedChart|Native|datetime|str|tuple[str|datetime|dict,str|tuple[float,float] |dict|None]) – Transit time - can be: - CalculatedChart: use as-is - Native: build chart from Native - datetime or str: use natal chart’s location - tuple[datetime, location]: build chart from tuplenatal_label (
str) – Label for natal charttransit_label (
str) – Label for transit chart
- Return type:
- Returns:
MultiChartBuilder configured for transits
Example
# Using a raw datetime (uses natal location) mc = MultiChartBuilder.transit(natal, datetime(2025, 1, 1, 12, 0))
# Using a tuple with explicit location mc = MultiChartBuilder.transit(natal, (datetime(2025, 1, 1), “New York”))
- with_aspect_engine(engine)[source]
Set the aspect engine for cross-chart aspects.
- Parameters:
engine – AspectEngine instance
- Return type:
- Returns:
Self for chaining
- with_cross_aspects(pairs='to_primary')[source]
Configure which chart pairs to calculate cross-aspects for.
- Parameters:
pairs (
Union[list[tuple[int,int]],Literal['all','to_primary','adjacent']]) – Either: - “to_primary”: Only aspects to chart[0] (default) - “adjacent”: Adjacent pairs (0-1, 1-2, 2-3) - “all”: All possible pairs - List of (i, j) tuples for explicit pairs- Return type:
- Returns:
Self for chaining
- with_house_overlays(enabled=True)[source]
Enable or disable house overlay calculation.
- Parameters:
enabled (
bool) – Whether to calculate house overlays- Return type:
- Returns:
Self for chaining
- with_internal_aspect_engine(engine)[source]
Set aspect engine for calculating internal (natal) aspects.
- Parameters:
engine – AspectEngine instance
- Return type:
- Returns:
Self for chaining
- with_internal_orb_engine(engine)[source]
Set orb engine for calculating internal (natal) aspects.
- Parameters:
engine (
OrbEngine) – OrbEngine instance- Return type:
- Returns:
Self for chaining
- with_labels(labels)[source]
Set labels for each chart.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_orb_engine(engine)[source]
Set the orb engine for cross-chart aspects.
- Parameters:
engine (
OrbEngine) – OrbEngine instance- Return type:
- Returns:
Self for chaining
- without_cross_aspects()[source]
Disable cross-aspect calculation.
- Return type:
- Returns:
Self for chaining
- without_house_overlays()[source]
Disable house overlay calculation.
- Return type:
- Returns:
Self for chaining
Synthesis (stellium.core.synthesis)¶
Synthesis charts: Composite and Davison chart calculations.
These create a single “synthesized” chart from two source charts, representing a relationship or combined energy.
Composite: Midpoint of each planet/point between charts Davison: Midpoint in time and space, then regular chart calculation
- class stellium.core.synthesis.SynthesisBuilder(chart1, chart2, method)[source]
Bases:
objectBuilder for synthesizing two charts into one (composite or davison).
Example:
# Simple davison davison = SynthesisBuilder.davison(chart1, chart2).calculate() # Configured composite composite = (SynthesisBuilder.composite(chart1, chart2) .with_midpoint_method("short_arc") .with_labels("Alice", "Bob") .calculate())
- calculate()[source]
Calculate the synthesis chart.
- Return type:
- Returns:
SynthesisChart (subclass of CalculatedChart)
- classmethod composite(chart1, chart2)[source]
Create composite chart (midpoint of all positions).
- Parameters:
chart1 (
CalculatedChart|Native) – First chart (CalculatedChart or Native)chart2 (
CalculatedChart|Native) – Second chart (CalculatedChart or Native)
- Return type:
- Returns:
SynthesisBuilder configured for composite calculation
- classmethod davison(chart1, chart2)[source]
Create davison chart (midpoint in time and space).
- Parameters:
chart1 (
CalculatedChart|Native) – First chart (CalculatedChart or Native)chart2 (
CalculatedChart|Native) – Second chart (CalculatedChart or Native)
- Return type:
- Returns:
SynthesisBuilder configured for davison calculation
- with_houses(houses)[source]
Set house calculation method for composite charts.
- Parameters:
houses (
bool|str) – True (default) - Derived ASC method (midpoint Ascendants, derive cusps) False - No houses (positions only) “place” - Reference place method (geographic midpoint + derived time)- Return type:
- Returns:
Self for chaining
Example
# No houses composite = SynthesisBuilder.composite(c1, c2).with_houses(False).calculate()
# Reference place method composite = SynthesisBuilder.composite(c1, c2).with_houses(“place”).calculate()
- with_labels(label1, label2)[source]
Set descriptive labels for source charts.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_location_method(method)[source]
Set location midpoint method for davison charts.
- Parameters:
method (
str) – “great_circle” (default) - Geodesic midpoint following Earth’s curvature “simple” - Arithmetic mean of lat/lon (faster but less accurate)- Return type:
- Returns:
Self for chaining
- class stellium.core.synthesis.SynthesisChart(datetime, location, positions, house_systems=<factory>, house_placements=<factory>, aspects=(), declination_aspects=(), metadata=<factory>, zodiac_type=None, ayanamsa=None, ayanamsa_value=None, calculation_timestamp=<factory>, chart_tags=(), synthesis_method='', source_chart1=None, source_chart2=None, chart1_label='Chart 1', chart2_label='Chart 2', midpoint_method=None, houses_config=None, location_method=None)[source]
Bases:
CalculatedChartA chart synthesized from two source charts (composite or davison).
Inherits all fields from CalculatedChart: - positions: tuple[CelestialPosition, …] - aspects: tuple[Aspect, …] - house_systems: dict[str, HouseCusps] - house_placements: dict[str, dict] - datetime: ChartDateTime - location: ChartLocation - metadata: dict
And adds synthesis-specific fields.
- chart1_label: str = 'Chart 1'
Descriptive label for first chart (e.g., “Alice”, “Natal”)
- chart2_label: str = 'Chart 2'
Descriptive label for second chart (e.g., “Bob”, “Transit”)
- houses_config: bool | str | None = None
True (derived), False (none), or “place”
- Type:
Composite only
- source_chart1: CalculatedChart | None = None
The first source chart (full chart object for reference)
- source_chart2: CalculatedChart | None = None
The second source chart (full chart object for reference)
- synthesis_method: str = ''
“composite” or “davison”
- Type:
The synthesis method used
- to_dict()[source]
Extend parent’s to_dict with synthesis-specific fields.
- to_prompt_text(sections=None, include_extras=True, include_source_charts=False)[source]
Export synthesis chart as prompt text.
- Parameters:
- Return type:
- Returns:
A multi-line string ready to paste into an LLM prompt.
- stellium.core.synthesis.calculate_datetime_midpoint(dt1, dt2)[source]
Calculate midpoint between two datetimes using Julian day.
- Parameters:
dt1 (
ChartDateTime) – First chart datetimedt2 (
ChartDateTime) – Second chart datetime
- Return type:
- Returns:
Tuple of (midpoint_datetime, midpoint_julian_day)
- stellium.core.synthesis.calculate_location_midpoint(loc1, loc2, method='great_circle')[source]
Calculate geographic midpoint between two locations.
- Parameters:
loc1 (
ChartLocation) – First locationloc2 (
ChartLocation) – Second locationmethod (
str) – “great_circle” (default, geodesic) or “simple” (arithmetic mean)
- Return type:
- Returns:
Midpoint location
Note
Great circle (geodesic) midpoint follows the Earth’s curvature and is more accurate for locations far apart. Simple arithmetic mean can give incorrect results, especially across the date line or for distant points.
- stellium.core.synthesis.calculate_midpoint_longitude(lon1, lon2, method='short_arc')[source]
Calculate midpoint between two zodiac longitudes.
- Parameters:
- Return type:
- Returns:
Midpoint longitude (0-360)
Examples
>>> calculate_midpoint_longitude(10, 20) # Both in Aries 15.0
>>> calculate_midpoint_longitude(10, 190) # Aries and Libra 100.0 # Cancer (short arc)
>>> calculate_midpoint_longitude(10, 190, "long_arc") 280.0 # Capricorn (long arc)
Configuration (stellium.core.config)¶
Configuration models for chart calculation.
- class stellium.core.config.AspectConfig(aspects=<factory>, include_angles=True, include_nodes=True, include_asteroids=True)[source]
Bases:
objectConfiguration for aspect calculations.
Will be passed directly into the AspectEngine.
- include_angles: bool = True
- include_asteroids: bool = True
- include_nodes: bool = True
- class stellium.core.config.CalculationConfig(include_planets=<factory>, include_nodes=True, include_chiron=True, include_points=<factory>, include_asteroids=<factory>, zodiac_type=ZodiacType.TROPICAL, ayanamsa=None, heliocentric=False)[source]
Bases:
objectOverall configuration for chart calculations.
Passed to the ChartBuilder.
- classmethod comprehensive()[source]
Comprehensive calculation – a well-rounded set.
- Return type:
CalculationConfig
- heliocentric: bool = False
- include_chiron: bool = True
- include_nodes: bool = True
- classmethod minimal()[source]
Minimal calculation - planets only.
- Return type:
CalculationConfig
- zodiac_type: ZodiacType = 'tropical'
Ayanamsa (stellium.core.ayanamsa)¶
Ayanamsa (sidereal offset) definitions and registry for Stellium.
This module provides the ZodiacType enum and a registry of ayanamsa systems used in sidereal astrology. Each ayanamsa represents a different calculation method for determining the offset between the tropical and sidereal zodiacs.
- class stellium.core.ayanamsa.AyanamsaInfo(name, swe_constant, description, tradition)[source]
Bases:
objectInformation about a specific ayanamsa system.
- name
Human-readable name of the ayanamsa
- swe_constant
Swiss Ephemeris constant for this ayanamsa
- description
Brief description of the system
- tradition
Tradition this ayanamsa belongs to (vedic, western_sidereal, etc.)
- description: str
- name: str
- swe_constant: int
- tradition: str
- class stellium.core.ayanamsa.ZodiacType(value)[source]
Bases:
EnumType of zodiac system used for calculations.
TROPICAL: Based on the seasons (0° Aries = March equinox) SIDEREAL: Based on fixed star positions (varies by ayanamsa)
- SIDEREAL = 'sidereal'
- TROPICAL = 'tropical'
- stellium.core.ayanamsa.get_ayanamsa(name)[source]
Get ayanamsa information by name.
- Parameters:
name (
str) – Name of the ayanamsa (case-insensitive, accepts spaces/hyphens)- Return type:
AyanamsaInfo- Returns:
AyanamsaInfo for the requested ayanamsa
- Raises:
ValueError – If ayanamsa name is not recognized
Examples
>>> info = get_ayanamsa("lahiri") >>> info.name 'Lahiri' >>> info = get_ayanamsa("Fagan-Bradley") # Case insensitive, hyphen ok >>> info.tradition 'western_sidereal'
- stellium.core.ayanamsa.get_ayanamsa_value(julian_day, ayanamsa)[source]
Calculate the ayanamsa offset value for a specific date.
The ayanamsa value represents the difference in degrees between the tropical and sidereal zodiacs at a given point in time.
- Parameters:
- Return type:
- Returns:
Ayanamsa offset in degrees
Example
>>> from stellium.utils.time import datetime_to_julian_day >>> from datetime import datetime >>> jd = datetime_to_julian_day(datetime(2000, 1, 1, 12, 0)) >>> offset = get_ayanamsa_value(jd, "lahiri") >>> print(f"Lahiri ayanamsa in 2000: {offset:.2f}°") Lahiri ayanamsa in 2000: 23.85°
Chart Utilities (stellium.core.chart_utils)¶
Utility functions for chart type handling.
This module provides centralized utilities for working with different chart types (CalculatedChart, MultiChart, Comparison) without requiring direct imports that could cause circular dependencies.
The functions use duck-typing (hasattr checks) where necessary to avoid importing MultiChart or Comparison directly, which helps prevent circular import issues.
- Example usage:
from stellium.core.chart_utils import get_all_charts, get_chart_labels
- def process_chart(chart):
charts = get_all_charts(chart) labels = get_chart_labels(chart) for c, label in zip(charts, labels):
print(f”{label}: {c.datetime}”)
- stellium.core.chart_utils.chart_count(chart)[source]
Get the number of charts.
- stellium.core.chart_utils.get_all_charts(chart)[source]
Get all charts as a list.
For single charts, returns [chart]. For MultiChart/MultiWheel, returns list(charts). For Comparison, returns [chart1, chart2].
- Parameters:
chart (
Any) – Any chart-like object- Return type:
- Returns:
List of CalculatedChart objects
- stellium.core.chart_utils.get_chart_at_index(chart, index)[source]
Get a specific chart by index.
- Parameters:
- Return type:
- Returns:
The CalculatedChart at the specified index
- Raises:
IndexError – If index is out of range
- stellium.core.chart_utils.get_chart_label_at_index(chart, index)[source]
Get the label for a specific chart by index.
- Parameters:
- Return type:
- Returns:
The label string for the chart at the specified index
- Raises:
IndexError – If index is out of range
- stellium.core.chart_utils.get_chart_labels(chart)[source]
Get labels for all charts.
Generates default labels (“Chart 1”, “Chart 2”, etc.) if none are set.
- stellium.core.chart_utils.get_primary_chart(chart)[source]
Extract the primary (first) chart from any chart type.
For single charts, returns the chart itself. For MultiChart/MultiWheel, returns charts[0]. For Comparison, returns chart1.
- Parameters:
chart (
Any) – Any chart-like object- Return type:
- Returns:
The primary CalculatedChart
- stellium.core.chart_utils.is_comparison(chart)[source]
Check if chart is a Comparison (deprecated type).
Uses duck-typing to avoid circular imports.
- stellium.core.chart_utils.is_multichart(chart)[source]
Check if chart is a MultiChart.
Uses duck-typing to avoid circular imports.
- stellium.core.chart_utils.is_multiwheel(chart)[source]
Check if chart is a MultiWheel (deprecated type).
Uses duck-typing to avoid circular imports.
- stellium.core.chart_utils.is_single_chart(chart)[source]
Check if chart is a single CalculatedChart (not UnknownTimeChart or multi-chart).
Multiwheel (stellium.core.multiwheel)¶
MultiWheel chart implementation for 2-4 chart comparisons.
This module provides a unified interface for rendering multiple charts concentrically inside a single zodiac wheel: - Biwheel (2 charts): Natal + transits, synastry, etc. - Triwheel (3 charts): Natal + progressed + transits - Quadwheel (4 charts): Maximum supported
Ring order (center → out): - Tiny aspect center (no aspect lines drawn) - Chart 1 ring (innermost) - houses + objects - Chart 2 ring - Chart 3 ring (if present) - Chart 4 ring (if present) - Zodiac ring (outermost)
Each chart ring includes: - Alternating house fills (theme-colored per chart) - House divider lines (full ring width) - Planet glyphs with compact info (degree only) - Position ticks on ring’s inner rim
- class stellium.core.multiwheel.MultiWheel(charts, labels=(), cross_aspects=<factory>, calculation_timestamp=<factory>)[source]
Bases:
objectMulti-chart comparison supporting 2-4 charts rendered concentrically.
All charts are rendered inside the zodiac ring, with Chart 1 as the innermost ring and subsequent charts expanding outward.
- charts
Tuple of 2-4 CalculatedChart objects
- labels
Optional labels for each chart (auto-generated if empty)
- cross_aspects
Dict mapping chart index pairs to their cross-aspects
- calculation_timestamp
When this MultiWheel was created
- calculation_timestamp: datetime
- property chart1: CalculatedChart
Primary chart (innermost ring).
- property chart2: CalculatedChart
Second chart.
- property chart3: CalculatedChart | None
Third chart (if present).
- property chart4: CalculatedChart | None
Fourth chart (if present).
- property chart_count: int
Number of charts in this MultiWheel.
- charts: tuple[CalculatedChart, ...]
- class stellium.core.multiwheel.MultiWheelBuilder(charts)[source]
Bases:
objectFluent builder for creating MultiWheel objects.
- Usage:
- multiwheel = (MultiWheelBuilder
.from_charts([natal, transit, progressed]) .with_labels([“Natal”, “Transit”, “Progressed”]) .calculate())
# Or simply: multiwheel = MultiWheelBuilder.from_charts([chart1, chart2]).calculate()
- calculate()[source]
Build the MultiWheel object.
- Return type:
MultiWheel- Returns:
Configured MultiWheel ready for visualization
- classmethod from_charts(charts)[source]
Create a MultiWheelBuilder from a list of calculated charts.
- Parameters:
charts (
list[CalculatedChart]) – List of 2-4 CalculatedChart objects- Return type:
MultiWheelBuilder- Returns:
MultiWheelBuilder ready for configuration
- with_cross_aspects()[source]
Enable cross-chart aspect calculation.
Note: This can be expensive for 3-4 charts as it calculates aspects between all chart pairs.
- Return type:
MultiWheelBuilder- Returns:
self for chaining
Protocols (stellium.core.protocols)¶
Interface definitions for extending Stellium.
Protocol definitions for Stellium components.
Protocols define INTERFACES - what methods a component must implement. They don’t provide implementation - that’s in the engine classes.
Think of these as contracts: “If you want to be an EphemerisEngine, you must implement these methods with these signatures.”
- class stellium.core.protocols.AspectEngine(*args, **kwargs)[source]
Bases:
ProtocolProtocol for aspect calculation engines.
Different implementations might use: - Traditional aspects (Ptolemaic) - Modern aspects (including minor aspects) - Harmonic aspects - Vedic aspects (completely different system)
- calculate_aspects(positions, orb_engine)[source]
Calculate aspects between celestial objects.
- Parameters:
positions (
list[CelestialPosition]) – Objects to find aspects betweenorb_engine (
OrbEngine) – Optional custom orb settings
- Return type:
- Returns:
List of Aspect objects
- class stellium.core.protocols.ChartAnalyzer(*args, **kwargs)[source]
Bases:
ProtocolProtocol for chart analysis components.
Analyzers examine a calculated chart and return findings.
- analyze(chart)[source]
Analyze the chart.
- Parameters:
chart (
CalculatedChart) – Chart to analyze- Return type:
- Returns:
Dict of findings (type depends on analyzer)
- property analyzer_name: str
Name of this analyzer.
- property metadata_name: str
Name that the metadata should be store under
- class stellium.core.protocols.ChartComponent(*args, **kwargs)[source]
Bases:
ProtocolBase protocol for chart calculation components.
Components can be: - Arabic part calculators - Midpoint finders - Pattern detectors (grand trine, T-square, etc.) - Fixed star calculators - Harmonic charts
- calculate(datetime, location, positions, house_systems_map, house_placements_map)[source]
Calculate additional chart objects.
- Parameters:
datetime (
ChartDateTime) – Chart datetimelocation (
ChartLocation) – Chart locationpositions (
list[CelestialPosition]) – Already calculated positionshouse_systems_map (
dict[str,HouseCusps]) – House cusps by systemhouse_placements_map (
dict[str,dict[str,int]]) – House placements by system then planet
- Return type:
- Returns:
List of additional CelestialPosition objects
- property component_name: str
Name of this component.
- metadata_name = ''
- class stellium.core.protocols.ChartLike(*args, **kwargs)[source]
Bases:
ProtocolProtocol for chart-like objects (single or multi-chart).
This protocol defines the common interface that all chart types should support, enabling code to work with CalculatedChart, MultiChart, or Comparison objects interchangeably where appropriate.
Note: The chart parameter in methods like get_object() defaults to 0, which works for single charts (ignored) and multi-charts (returns from first chart).
- property datetime: ChartDateTime
The primary datetime of the chart (or first chart for multi-charts).
- draw(filename='chart.svg')[source]
Create a visualization builder for this chart.
- Parameters:
filename (
str) – Default filename for saving- Return type:
ChartDrawBuilder- Returns:
ChartDrawBuilder configured for this chart
- get_object(name, chart=0)[source]
Get a celestial object by name.
- Parameters:
- Return type:
- Returns:
The CelestialPosition if found, None otherwise
- get_planets(chart=0)[source]
Get all planetary positions.
- Parameters:
chart (
int) – For multi-charts, which chart to query (0-indexed). Ignored for single charts.- Return type:
- Returns:
List of planet CelestialPosition objects
- property location: ChartLocation
The primary location of the chart (or first chart for multi-charts).
- class stellium.core.protocols.CrossChartAspectEngine(*args, **kwargs)[source]
Bases:
ProtocolProtocol for calculating aspects between two charts.
This is separate from AspectEngine to allow different orb configurations and aspect sets for cross-chart work.
Use cases: - Synastry: Person A’s planets aspecting Person B’s planets - Transits: Current sky aspecting natal chart - Progressions: Progressed chart aspecting natal chart
- calculate_cross_aspects(chart1_positions, chart2_positions, orb_engine)[source]
Calculate aspects between two sets of positions.
Only calculates aspects where one object is from chart1 and the other is from chart2. Internal aspects within each chart are not calculated.
- Parameters:
chart1_positions (
list[CelestialPosition]) – Positions from first chart (e.g., natal/inner)chart2_positions (
list[CelestialPosition]) – Positions from second chart (e.g., transit/outer)orb_engine (
OrbEngine) – The OrbEngine that will provide orb allowances
- Return type:
- Returns:
List of Aspect objects representing cross-chart aspects
- class stellium.core.protocols.DignityCalculator(*args, **kwargs)[source]
Bases:
ProtocolProtocol for dignity/debility calculation.
Different implementations: - Traditional essential dignities - Modern rulerships - Vedic dignity system
- calculate_dignities(position)[source]
Calculate dignities for a celestial position.
- Parameters:
position (
CelestialPosition) – Position to calculate dignities for- Return type:
- Returns:
Dictionary with dignity information
- class stellium.core.protocols.EphemerisEngine(*args, **kwargs)[source]
Bases:
ProtocolProtocol for planetary position calculation engines.
Different implementations might use: - Swiss Ephemeris - JPL Ephemeris - Custom calculation algorithms - Mock data for testing
- calculate_positions(datetime, location, objects=None, config=None)[source]
Calculate positions for celestial objects.
- Parameters:
datetime (
ChartDateTime) – When to calculate positionslocation (
ChartLocation) – Where to calculate from (for topocentric)objects (
list[str] |None) – Which objects to calculate (None = all standard objects)config (
CalculationConfig|None) – Optional calculation configuration (zodiac type, etc.)
- Return type:
- Returns:
List of CelestialPosition objects
- class stellium.core.protocols.HouseSystemEngine(*args, **kwargs)[source]
Bases:
ProtocolProtocol for house system calculation engines.
Different implementations for different house systems: - Whole Sign - Placidus - Koch - Equal House - etc
- assign_houses(positions, cusps)[source]
Assign house numbers to celestial positions.
- Parameters:
positions (
list[CelestialPosition]) – Celestial objects to assign housescusps (
HouseCusps) – House cusps to use for assignment
- Returns:
house_number}
- Return type:
A dictionary of {object_name
- calculate_house_data(datetime, location)[source]
Calculate house cusps for this system.
- Parameters:
datetime (
ChartDateTime) – Chart datetimelocation (
ChartLocation) – Chart location
- Returns:
HouseCusps object with 12 cusp positions (For this specific system)
A List of CelestialPosition objects for the primary angles (ASC, MC, DSC, IC, Vertex)
- Return type:
Tuple containing
- property system_name: str
Name of this house system (e.g. Placidus)
- class stellium.core.protocols.OrbEngine(*args, **kwargs)[source]
Bases:
ProtocolProtocol for orb calculation.
Encapsulates logic for determining orb allowance, which can be simple (by aspect) or complex (by planet, by planet pair, by day/night, etc.).
- get_orb_allowance(obj1, obj2, aspect_name)[source]
Get the allowed orb for a specific aspect between two objects.
- Parameters:
obj1 (
CelestialPosition) – The first celestial objectobj2 (
CelestialPosition) – The second celestial objectaspect_name (
str) – The name of the aspect (e.g. Square)
- Return type:
- Returns:
The maximum allowed orb in degrees
- class stellium.core.protocols.ReportRenderer(*args, **kwargs)[source]
Bases:
ProtocolProtocol for output renderers.
Renderers take structured section data and format it for a specific output medium (terminal, plain text, HTML, etc.).
Why separate renderers? - Same data, multiple output formats - Easy to add new formats without touching section code - Testable in isolation
- render_report(sections)[source]
Render a complete report with multiple sections.
- class stellium.core.protocols.ReportSection(*args, **kwargs)[source]
Bases:
ProtocolProtocol for report sections.
Each section knows how to extract data from a chart (single or multi-chart) and format it into a standardized structure that renderers can consume.
Multi-Chart Support: Sections may receive any of: - CalculatedChart: Single natal/event chart - Comparison: Two-chart comparison (deprecated, use MultiChart) - MultiChart: 2-4 charts for synastry, transits, progressions, etc.
Implementations should use stellium.core.chart_utils helpers to handle different chart types consistently: - get_all_charts(chart) - Get list of all charts - get_chart_labels(chart) - Get labels for each chart - chart_count(chart) - Get number of charts
Why a protocol? - Extensibility: Users can create custom sections - Type safety: MyPy/Pyright can verify implementations - No inheritance required: Keep components lightweight
- generate_data(chart)[source]
Extract and structure data from the chart.
Returns a standardized dictionary format that renderers understand:
{ "type": "table" | "text" | "key_value" | "side_by_side_tables" | "grouped_tables", "headers": [...], # For tables "rows": [...], # For tables "text": "...", # For text blocks "data": {...}, # For key-value pairs "tables": [...], # For side_by_side_tables or grouped_tables }
- property section_name: str
Human-readable name for this section.
Used as a header in rendered output.
Engines (stellium.engines)¶
Calculation engines for ephemeris, houses, aspects, orbs, dignities, and fixed stars.
Ephemeris calculation engines.
- class stellium.engines.ephemeris.MockEphemerisEngine(mock_data=None)[source]
Bases:
objectMock ephemeris engine for testing.
Returns fixed positions instead of calculating them.
Useful for: - Unit tests - Development - Benchmarking other components
- calculate_positions(datetime, location, objects=None, config=None)[source]
Return mock positions.
- Parameters:
datetime (
ChartDateTime) – When to calculate positions (ignored in mock)location (
ChartLocation) – Where to calculate from (ignored in mock)objects (
list[str] |None) – Which objects to calculate (None = all mock objects)config (
CalculationConfig|None) – Calculation config (ignored in mock)
- Return type:
- Returns:
List of mock CelestialPosition objects
- class stellium.engines.ephemeris.SwissEphemerisEngine(ephe_path=None)[source]
Bases:
objectSwiss Ephemeris calculation engine.
This is our default, high-precision ephemeris calculator. Uses the pyswisseph library for accurate planetary positions.
- calculate_positions(datetime, location, objects=None, config=None)[source]
Calculate positions using Swiss Ephemeris.
- Parameters:
datetime (
ChartDateTime) – When to calculatelocation (
ChartLocation) – Where to calculate fromobjects (
list[str] |None) – Which objects to calculate (None = all standard)config (
CalculationConfig|None) – Calculation configuration (for zodiac type)
- Return type:
- Returns:
List of CelestialPosition objects
House system calculation engines.
- class stellium.engines.houses.APCHouses[source]
Bases:
SwissHouseSystemBaseAPC house system engine.
- property system_name: str
- class stellium.engines.houses.AlcabitiusHouses[source]
Bases:
SwissHouseSystemBaseAlcabitius house system engine.
- property system_name: str
- class stellium.engines.houses.AxialRotationHouses[source]
Bases:
SwissHouseSystemBaseAxial Rotation house system engine.
- property system_name: str
- class stellium.engines.houses.CampanusHouses[source]
Bases:
SwissHouseSystemBaseCampanus house system engine.
- property system_name: str
- class stellium.engines.houses.EqualHouses[source]
Bases:
SwissHouseSystemBaseEqual house system engine.
- property system_name: str
- class stellium.engines.houses.EqualMCHouses[source]
Bases:
SwissHouseSystemBaseEqual (MC) house system engine.
- property system_name: str
- class stellium.engines.houses.EqualVertexHouses[source]
Bases:
SwissHouseSystemBaseEqual (Vertex) house system engine.
- property system_name: str
- class stellium.engines.houses.GauquelinHouses[source]
Bases:
SwissHouseSystemBaseGauquelin house system engine.
- property system_name: str
- class stellium.engines.houses.HorizontalHouses[source]
Bases:
SwissHouseSystemBaseHorizontal house system engine.
- property system_name: str
- class stellium.engines.houses.KochHouses[source]
Bases:
SwissHouseSystemBaseKoch house system engine.
- property system_name: str
- class stellium.engines.houses.KrusinskiHouses[source]
Bases:
SwissHouseSystemBaseKrusinski house system engine.
- property system_name: str
- class stellium.engines.houses.MorinusHouses[source]
Bases:
SwissHouseSystemBaseMorinus house system engine.
- property system_name: str
- class stellium.engines.houses.PlacidusHouses[source]
Bases:
SwissHouseSystemBasePlacidus house system engine.
- property system_name: str
- class stellium.engines.houses.PorphyryHouses[source]
Bases:
SwissHouseSystemBasePorphyry house system engine.
- property system_name: str
- class stellium.engines.houses.RegiomontanusHouses[source]
Bases:
SwissHouseSystemBaseRegiomontanus house system engine.
- property system_name: str
- class stellium.engines.houses.SwissHouseSystemBase[source]
Bases:
objectProvides a default implementation for calling swisseph and assigning houses.
This is NOT a protocol, just a helper class for code reuse.
- assign_houses(positions, cusps)[source]
Assign house numbers to positions. Returns a simple name: house dict.
- calculate_house_data(datetime, location, config=None)[source]
Calculate house system’s house cusps and chart angles.
- Parameters:
datetime (
ChartDateTime) – Chart datetimelocation (
ChartLocation) – Chart locationconfig (
CalculationConfig|None) – Calculation configuration (for zodiac type)
- Return type:
- Returns:
Tuple of (house cusps, angle positions)
- property system_name: str
- class stellium.engines.houses.TopocentricHouses[source]
Bases:
SwissHouseSystemBaseTopocentric house system engine.
- property system_name: str
- class stellium.engines.houses.VehlowEqualHouses[source]
Bases:
SwissHouseSystemBaseVehlow Equal house system engine.
- property system_name: str
- class stellium.engines.houses.WholeSignHouses[source]
Bases:
SwissHouseSystemBaseWhole sign house system engine.
- property system_name: str
Aspect calculation engines.
These engines are responsible for finding angular relationships (aspects) between celestial objects. They follow the AspectEngine protocol.
- class stellium.engines.aspects.CrossChartAspectEngine(config=None)[source]
Bases:
objectCalculate aspects between two separate charts.
Unlike ModernAspectEngine which finds all aspects within a single chart (using combinations of all positions), this engine specifically handles cross-chart scenarios where we want aspects BETWEEN chart1 objects and chart2 objects (but not within each chart).
Use cases: - Synastry: Person A’s planets aspecting Person B’s planets - Transits: Current sky aspecting natal chart - Progressions: Progressed chart aspecting natal chart
The key difference: controlled iteration. We only check pairs where one object is from chart1 and the other is from chart2. This prevents: - Object identity collision (same planet in both charts) - Redundant calculation (internal aspects already calculated separately) - Incorrect filtering (can’t distinguish sources after merging lists)
- calculate_cross_aspects(chart1_positions, chart2_positions, orb_engine)[source]
Calculate aspects between two sets of positions.
This only calculates aspects WHERE one object is from chart1 and the other is from chart2. Internal aspects within each chart are NOT calculated by this method.
- Parameters:
chart1_positions (
list[CelestialPosition]) – Positions from first chart (e.g., natal/inner)chart2_positions (
list[CelestialPosition]) – Positions from second chart (e.g., transit/outer)orb_engine (
OrbEngine) – The OrbEngine that will provide orb allowances
- Return type:
- Returns:
List of Aspect objects representing cross-chart aspects
Example
>>> engine = CrossChartAspectEngine() >>> orb_engine = SimpleOrbEngine() >>> aspects = engine.calculate_cross_aspects( ... natal_chart.positions, ... transit_chart.positions, ... orb_engine ... ) >>> # Gets natal Sun trine transit Jupiter, etc. >>> # Does NOT get natal Sun trine natal Moon (internal)
- class stellium.engines.aspects.DeclinationAspectEngine(orb=1.0, include_types=None)[source]
Bases:
objectCalculates declination aspects (Parallel and Contraparallel).
Declination aspects are based on celestial equatorial coordinates: - Parallel: Two bodies at the SAME declination (both north or both south).
Interpreted similarly to a conjunction - blending of energies.
Contraparallel: Two bodies at the SAME declination magnitude but OPPOSITE hemispheres. Interpreted similarly to an opposition - polarity.
Unlike longitude-based aspects which use variable orbs by planet, declination aspects traditionally use a fixed tight orb (1.0-1.5 degrees).
Example
>>> engine = DeclinationAspectEngine(orb=1.0) >>> aspects = engine.calculate_aspects(chart.positions) >>> for asp in aspects: ... print(asp) Sun Parallel Moon (orb: 0.45°) Mars Contraparallel Saturn (orb: 0.78°)
- calculate_aspects(positions, orb_engine=None)[source]
Calculate parallel and contraparallel aspects.
- Parameters:
positions (
list[CelestialPosition]) – List of CelestialPosition objects to check. Only positions with non-None declination are used.orb_engine (
OrbEngine|None) – Ignored. Declination aspects use fixed orb. Included for compatibility with AspectEngine protocol.
- Return type:
- Returns:
List of Aspect objects for detected declination aspects.
- class stellium.engines.aspects.HarmonicAspectEngine(harmonic)[source]
Bases:
objectCalculates harmonic aspects (eg H5, H7, H9). This engine does not use AspectConfig, as it defines its own angles. It does use the OrbEngine, which can be configured to give different orbs for different harmonics.
- calculate_aspects(positions, orb_engine)[source]
Calculate harmonic aspects for the configured harmonic number.
Currently only calculates between ObjectType=Planet objects.
- Parameters:
positions (
list[CelestialPosition]) – The list of CelestialPositions objects to check.orb_engine (
OrbEngine) – The OrbEngine that will provide the orb allowance.
- Return type:
- Returns:
A list of found Aspect objects.
- class stellium.engines.aspects.ModernAspectEngine(config=None)[source]
Bases:
objectCalculates standard aspects (conjunction, square, trine, etc.) based on a provided AspectConfig.
- calculate_aspects(positions, orb_engine)[source]
Calculate aspects based on the engine’s config and the provided orb engine.
- Parameters:
positions (
list[CelestialPosition]) – The list of CelestialPosition objects to check.orb_engine (
OrbEngine) – The OrbEngine that will provide the orb allowance for each potential aspect.
- Return type:
- Returns:
A list of found Aspect objects.
Orb Calculation Engines.
These engines implement the OrbEngine protocol to provide different systems for calculating aspect orbs.
- class stellium.engines.orbs.ComplexOrbEngine(config)[source]
Bases:
objectImplements OrbEngine with a cascading priority matrix.
This engine can handle the most complex traditions by allowing orbs to be defined by pair, by single planet, or by aspect.
The config is a nested dictionary defining the priority.
- class stellium.engines.orbs.LuminariesOrbEngine(luminary_orbs=None, default_orbs=None, fallback_orb=None)[source]
Bases:
objectImplements OrbEngine with special rules for Luminaries.
This is a common system where aspects to the Sun or Moon are given a wider orb than aspects to other planets.
- class stellium.engines.orbs.MoietyOrbEngine(orb_map=None, system=None, fallback_orb=None, minor_aspect_multiplier=None)[source]
Bases:
objectImplements OrbEngine using the traditional moiety (half-orb) system.
Each planet has its own full orb value. The effective orb for an aspect between two planets is the average of their full orbs:
effective_orb = (full_orb_A + full_orb_B) / 2
This is the universal formula across all traditional sources from Sahl ibn Bishr (820 CE) through William Lilly (1647).
Supports named systems (“lilly”, “ptolemy”) for preset values, or custom orb maps for full control.
Example:
# Default (Lilly / medieval consensus) engine = MoietyOrbEngine() # Ptolemaic (larger orbs) engine = MoietyOrbEngine(system="ptolemy") # Custom values engine = MoietyOrbEngine(orb_map={"Sun": 15, "Moon": 12, "Mars": 7}) # With tighter orbs for minor/harmonic aspects engine = MoietyOrbEngine(minor_aspect_multiplier=0.4)
- class stellium.engines.orbs.SimpleOrbEngine(orb_map=None, fallback_orb=None)[source]
Bases:
objectImplements OrbEngine for simple, aspect-based orbs.
This engine uses a single dictionary mapping an aspect name to an orb value, regardless of the planets involved.
This is the default engine used by ChartBuilder.
Dignity calculation engines.
This module provides comprehensive essential dignity calculations for both traditional (pre-1781) and modern astrological systems. It evaluates planetary strength through rulership, exaltation, detriment, fall, triplicity, terms/bounds, faces/decans, and mutual reception.
- class stellium.engines.dignities.ModernDignityCalculator(decans='chaldean')[source]
Bases:
objectModern essential dignities calculator (post-1781).
Includes outer planets (Uranus, Neptune, Pluto) and uses modern rulership assignments. The scoring system is adapted for modern rulerships while maintaining traditional dignity principles.
Scoring system: - Domicile/Rulership: +5 points (modern ruler), +3 points (traditional ruler) - Exaltation: +4 points - Triplicity ruler: +3 points - Term/Bound ruler: +2 points - Face/Decan ruler: +1 point - Detriment: -5 points (modern), -3 points (traditional) - Fall: -4 points
- MODERN_PLANETS = ['Sun', 'Moon', 'Mercury', 'Venus', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto']
- calculate_dignities(position, sect='day')[source]
Calculate modern dignities for a position.
- Parameters:
position (
CelestialPosition) – CelestialPosition to analyzeis_day_chart – Whether this is a day chart (Sun above horizon). Affects triplicity ruler selection.
- Return type:
- Returns:
Dictionary with comprehensive dignity information.
- property calculator_name: str
Name of this calculator.
- class stellium.engines.dignities.MutualReceptionAnalyzer(system='traditional')[source]
Bases:
objectAnalyze mutual reception between planets in a chart.
Mutual reception occurs when two planets are each in a sign ruled or exalted by the other. This creates a special bond and can modify the expression of both planets.
- class stellium.engines.dignities.TraditionalDignityCalculator(decans='chaldean')[source]
Bases:
objectTraditional essential dignities calculator (pre-1781).
Uses only the seven traditional planets (Sun through Saturn) and calculates dignities according to classical astrological principles.
Scoring system: - Domicile/Rulership: +5 points - Exaltation: +4 points - Triplicity ruler: +3 points - Term/Bound ruler: +2 points - Face/Decan ruler: +1 point - Detriment: -5 points - Fall: -4 points - Peregrine (no dignities): 0 points
- TRADITIONAL_PLANETS = ['Sun', 'Moon', 'Mercury', 'Venus', 'Mars', 'Jupiter', 'Saturn']
- calculate_dignities(position, sect='day')[source]
Calculate traditional dignities for a position.
- Parameters:
position (
CelestialPosition) – CelestialPosition to analyzesect (
str|None) – Chart sect. Can be “day” or “night” (defaults to day)
- Returns:
dignities: List of dignity types held
score: Total dignity score
details: Breakdown of each dignity
is_peregrine: Whether planet is peregrine (no dignities)
reception_potential: Planets this one could have mutual reception with
- Return type:
Dictionary with comprehensive dignity information including
- property calculator_name: str
Name of this calculator
Fixed Stars calculation engine using Swiss Ephemeris.
This module provides the engine for calculating fixed star positions at any given time. Swiss Ephemeris handles precession automatically - just pass the Julian Day and it returns the correct ecliptic longitude for that epoch.
- Usage:
>>> from stellium.engines.fixed_stars import SwissEphemerisFixedStarsEngine >>> engine = SwissEphemerisFixedStarsEngine() >>> stars = engine.calculate_stars(julian_day=2451545.0) # All registered stars >>> royal_stars = engine.calculate_stars(julian_day, stars=["Regulus", "Aldebaran"])
- class stellium.engines.fixed_stars.FixedStarsEngine(*args, **kwargs)[source]
Bases:
ProtocolProtocol for fixed star calculation engines.
Implementations must provide a method to calculate positions for fixed stars at a given Julian Day.
- calculate_stars(julian_day, stars=None)[source]
Calculate positions for specified fixed stars.
- class stellium.engines.fixed_stars.SwissEphemerisFixedStarsEngine(registry=None)[source]
Bases:
objectSwiss Ephemeris implementation of fixed star calculations.
Uses swe.fixstar_ut() to calculate precise ecliptic positions for fixed stars, with automatic precession handling.
The engine pulls metadata from FIXED_STARS_REGISTRY to enrich the position objects with traditional astrological meanings.
- registry
The fixed star registry to use (defaults to FIXED_STARS_REGISTRY)
Example
>>> engine = SwissEphemerisFixedStarsEngine() >>> # Calculate all stars >>> all_stars = engine.calculate_stars(julian_day=2451545.0) >>> # Calculate specific stars >>> royal = engine.calculate_stars(2451545.0, stars=["Regulus", "Aldebaran"]) >>> print(f"{royal[0].name}: {royal[0].sign_position}") Regulus: 29°50' Leo
- calculate_royal_stars(julian_day)[source]
Calculate positions for the four Royal Stars of Persia.
A convenience method for getting just the most important stars: Aldebaran, Regulus, Antares, and Fomalhaut.
- Parameters:
julian_day (
float) – The Julian Day for calculation- Return type:
- Returns:
List of FixedStarPosition objects for the four royal stars
- calculate_stars(julian_day, stars=None)[source]
Calculate positions for specified fixed stars.
- Parameters:
- Return type:
- Returns:
List of FixedStarPosition objects with calculated ecliptic positions and registry metadata.
- Raises:
ValueError – If a requested star is not in the registry
- calculate_stars_by_tier(julian_day, tier)[source]
Calculate positions for all stars of a specific tier.
- Parameters:
- Return type:
- Returns:
List of FixedStarPosition objects for stars of the specified tier
- get_magnitude(star_name)[source]
Get the apparent magnitude of a star.
This uses the registry value rather than calling swe.fixstar_mag() since we’ve already populated magnitude in FixedStarInfo.
Aspect Pattern Analyzer Engine.
Finds major aspect patterns like Grand Trines, T-Squares, Yods, Kites, Grand Crosses, and Stelliums. This module implements the “Analyzer” protocol.
- class stellium.engines.patterns.AspectPatternAnalyzer(stellium_min=3)[source]
Bases:
objectImplements the Analyzer protocol to find major aspect patterns. The results are stored in chart.metadata[‘aspect_patterns’]
- analyze(chart)[source]
Runs all pattern detectors and returns a list of findings serialized as dictionaries for metadata.
- Return type:
- property analyzer_name: str
- property metadata_name: str
Profection calculation engine.
Profections are a Hellenistic timing technique where the Ascendant (and other points) move forward one sign per year of life. The planet ruling the profected sign becomes the “Lord of the Year” - a key focus for that year’s themes.
Example
>>> from stellium import ChartBuilder
>>> from stellium.engines.profections import ProfectionEngine
>>>
>>> chart = ChartBuilder.from_notable("Albert Einstein").calculate()
>>> engine = ProfectionEngine(chart)
>>>
>>> # Annual profection for age 30
>>> result = engine.annual(30)
>>> print(f"Age 30: {result.profected_sign} year, Lord = {result.ruler}")
>>>
>>> # Multi-point profections
>>> results = engine.multi(30, points=["ASC", "Sun", "Moon", "MC"])
>>> print(f"Lords: {results.lords}")
- class stellium.engines.profections.MultiProfectionResult(age, date, results)[source]
Bases:
objectProfections from multiple points for the same time period.
Useful for seeing all the lords at once - e.g., who rules the profected ASC, Sun, Moon, MC, and Fortune for age 30.
- age
The age for these profections
- date
Optional specific date (for monthly profections)
- results
Dictionary of ProfectionResult keyed by point name
- age: int
- results: dict[str, ProfectionResult]
- class stellium.engines.profections.ProfectionEngine(chart, house_system=None, rulership='traditional')[source]
Bases:
objectGeneral-purpose profection calculator.
Profections move a point forward one sign per unit of time (year, month, day). This engine handles all the complexity of looking up houses, rulers, and finding what planets are activated.
- Parameters:
chart (
CalculatedChart) – The natal chart to profect fromhouse_system (
str|None) – House system to use (default “Whole Sign” - traditional)rulership (
Literal['traditional','modern']) – Rulership system (“traditional” or “modern”)
Example
>>> engine = ProfectionEngine(chart) >>> result = engine.annual(30) # Age 30 profection >>> print(result.ruler) # Lord of the Year
- DEFAULT_POINTS = ['ASC', 'Sun', 'Moon', 'MC']
- annual(age, point='ASC')[source]
Annual profection for a given age.
This is the most common use case: what house and lord are activated for a specific year of life?
- Parameters:
- Return type:
- Returns:
ProfectionResult for that age
Example
>>> result = engine.annual(30) >>> print(f"At age 30: {result.profected_sign} year") >>> print(f"Lord of the Year: {result.ruler}")
- for_date(date, point='ASC', include_monthly=True)[source]
Calculate profections for a specific date.
If include_monthly is True, returns both annual and monthly profection.
- Parameters:
- Return type:
ProfectionResult|tuple[ProfectionResult,ProfectionResult]- Returns:
ProfectionResult, or tuple of (annual, monthly) if include_monthly
Example
>>> annual, monthly = engine.for_date("2025-06-15") >>> print(f"Year: {annual.ruler}, Month: {monthly.ruler}")
- lord_of_month(age, month, point='ASC')[source]
Convenience: just get the Lord of the Month.
- lord_of_year(age, point='ASC')[source]
Convenience: just get the Lord of the Year.
- Parameters:
- Return type:
- Returns:
Name of the ruling planet
Example
>>> print(engine.lord_of_year(30)) # "Saturn"
- monthly(age, month, point='ASC')[source]
Monthly profection within a given year.
Profects forward by (age + month) signs total.
- Parameters:
- Return type:
- Returns:
ProfectionResult for that month
Example
>>> # 4th month of age 30 year >>> result = engine.monthly(age=30, month=4) >>> print(f"Month 4: {result.profected_sign}")
- multi(age, points=None)[source]
Profect multiple points at once.
Useful for seeing all the lords for a given age - who rules the profected ASC, Sun, Moon, MC?
- Parameters:
- Return type:
- Returns:
MultiProfectionResult with all profections
Example
>>> results = engine.multi(30) >>> print(results.lords) # {"ASC": "Saturn", "Sun": "Mars", ...}
- multi_for_date(date, points=None)[source]
Profect multiple points for a specific date.
- profect(point, units, unit_type='year')[source]
Core profection operation.
Profects any point forward by N signs and returns everything you’d want to know about the result.
- Parameters:
- Return type:
- Returns:
ProfectionResult with full details
Example
>>> result = engine.profect("ASC", units=30, unit_type="year") >>> print(f"House {result.profected_house}: {result.profected_sign}")
- timeline(start_age, end_age, point='ASC')[source]
Generate profections for a range of ages.
Useful for seeing the sequence of lords through life, or for timeline visualizations.
- Parameters:
- Return type:
- Returns:
ProfectionTimeline with all entries
Example
>>> timeline = engine.timeline(25, 35) >>> for entry in timeline.entries: ... print(f"Age {entry.units}: {entry.ruler}")
- class stellium.engines.profections.ProfectionResult(source_point, source_sign, source_house, units, unit_type, profected_house, profected_sign, ruler, ruler_position, ruler_house, ruler_modern, planets_in_house=<factory>)[source]
Bases:
objectResult of profecting a single point.
Contains everything you’d want to know about a profection: what was profected, where it landed, who rules it, and what’s there.
- source_point
Name of the profected point (“ASC”, “Sun”, etc.)
- source_sign
The sign the point is in natally
- source_house
The house the point is in natally (1-12)
- units
How many signs forward the point moved
- unit_type
Type of profection (“year”, “month”, “day”)
- profected_house
The house that is activated (1-12)
- profected_sign
The sign on that house cusp
- ruler
Traditional ruler of the profected sign (Lord of Year/Month)
- ruler_position
Natal position of the ruling planet
- ruler_house
Which house the ruler is in natally
- ruler_modern
Modern ruler if different from traditional
- planets_in_house
List of natal planets in the profected house
- planets_in_house: tuple[CelestialPosition, ...]
- profected_house: int
- profected_sign: str
- ruler: str
- ruler_position: CelestialPosition | None
- source_house: int
- source_point: str
- source_sign: str
- unit_type: str
- units: int
- class stellium.engines.profections.ProfectionTimeline(point, start_age, end_age, entries)[source]
Bases:
objectA range of profections over time.
Useful for seeing the sequence of lords through a span of life, or for displaying in a timeline visualization.
- point
The point being profected (e.g., “ASC”)
- start_age
First age in the timeline
- end_age
Last age in the timeline
- entries
List of ProfectionResult for each age
- end_age: int
- entries: tuple[ProfectionResult, ...]
- find_by_lord(lord)[source]
Find all years ruled by a specific planet.
- Return type:
- point: str
- start_age: int
- stellium.engines.profections.get_sign_ruler(sign, system='traditional')[source]
Get the planetary ruler of a zodiac sign.
- stellium.engines.profections.index_to_sign(index)[source]
Convert index to sign name, wrapping around.
- Return type:
- stellium.engines.profections.sign_to_index(sign)[source]
Convert sign name to index (0-11).
- Return type:
Dispositor graph calculation engine.
Dispositors trace the “chain of command” in a chart - each planet is disposed by the ruler of the sign it occupies. Following these chains reveals:
Planetary Dispositors: Which planets “dispose” (rule over) which others. The final dispositor is the planet that rules its own sign (e.g., Mars in Aries).
House Dispositors (Kate’s innovation): Which life areas flow into which others. “What planet rules this house’s cusp, and what house is THAT planet in?” The final dispositor house is the life area that supports/feeds the others.
Example
>>> from stellium import ChartBuilder
>>> from stellium.engines.dispositors import DispositorEngine
>>>
>>> chart = ChartBuilder.from_notable("Albert Einstein").calculate()
>>> engine = DispositorEngine(chart)
>>>
>>> # Planetary dispositors
>>> planetary = engine.planetary()
>>> print(f"Final dispositor: {planetary.final_dispositor}")
>>> print(f"Mutual receptions: {planetary.mutual_receptions}")
>>>
>>> # House dispositors (Kate's innovation)
>>> houses = engine.house_based()
>>> print(f"Final dispositor house: {houses.final_dispositor}")
- class stellium.engines.dispositors.DispositorEdge(source, target, source_sign, ruler)[source]
Bases:
objectA single edge in the dispositor graph.
For planetary: “Sun in Leo is disposed by Sun” (self-disposing) For house: “House 10 (Capricorn) flows to House 11 (where Saturn is)”
- ruler: str
- source: str
- source_sign: str
- target: str
- class stellium.engines.dispositors.DispositorEngine(chart, rulership_system='traditional', house_system=None)[source]
Bases:
objectCalculate dispositor graphs for a chart.
Supports two modes: - Planetary: Traditional planet-rules-planet dispositors - House: Kate’s innovation - life-area-flows-to-life-area dispositors
Example
>>> chart = ChartBuilder.from_notable("Albert Einstein").calculate() >>> engine = DispositorEngine(chart) >>> >>> # Planetary dispositors >>> p = engine.planetary() >>> print(p.final_dispositor) >>> >>> # House dispositors >>> h = engine.house_based() >>> print(h.final_dispositor)
- house_based()[source]
Calculate house-based dispositor graph (Kate’s innovation).
For each house: find the ruler of the sign on its cusp, then find what house that ruling planet is in.
This shows how life areas flow into and support each other.
- Return type:
DispositorResult- Returns:
DispositorResult with house-based dispositor analysis
- planetary()[source]
Calculate planetary dispositor graph.
Each planet is disposed by the ruler of the sign it occupies. A planet in its own sign (e.g., Mars in Aries) is self-disposing.
- Return type:
DispositorResult- Returns:
DispositorResult with planetary dispositor analysis
- class stellium.engines.dispositors.DispositorResult(mode, edges, final_dispositor, mutual_receptions, chains, rulership_system)[source]
Bases:
objectComplete dispositor analysis result.
Contains the full graph structure, final dispositor(s), mutual receptions, and chains for analysis.
- mode
“planetary” or “house”
- edges
All edges in the dispositor graph
- final_dispositor
The node(s) where all chains terminate (or None if loops)
- mutual_receptions
List of mutual reception pairs
- chains
Dict mapping each starting node to its full chain
- rulership_system
“traditional” or “modern”
- edges: tuple[DispositorEdge, ...]
- mode: Literal['planetary', 'house']
- mutual_receptions: tuple[MutualReception, ...]
- rulership_system: str
- class stellium.engines.dispositors.MutualReception(node1, node2, planet1=None, planet2=None)[source]
Bases:
objectTwo nodes that dispose each other.
Planetary: Mars in Capricorn ↔ Saturn in Aries (each rules the other’s sign) House: House 9 ↔ House 11 (their rulers are in each other’s houses)
- node1: str
- node2: str
- stellium.engines.dispositors.get_sign_ruler(sign, system='traditional')[source]
Get the planetary ruler of a zodiac sign.
- stellium.engines.dispositors.render_both_dispositors(planetary, house, *, use_glyphs=True)[source]
Render both planetary and house dispositors as subgraphs in a single SVG.
- Parameters:
planetary (
DispositorResult) – Planetary DispositorResulthouse (
DispositorResult) – House-based DispositorResultuse_glyphs (
bool) – Use planet glyphs for planetary graph
- Return type:
Digraph- Returns:
graphviz.Digraph with both graphs as labeled clusters
Example
>>> engine = DispositorEngine(chart) >>> planetary = engine.planetary() >>> house = engine.house_based() >>> graph = render_both_dispositors(planetary, house) >>> graph.render("dispositors", format="svg")
- stellium.engines.dispositors.render_dispositor_graph(result, *, use_glyphs=True, title=None)[source]
Render a single dispositor result as a graphviz Digraph.
- Parameters:
- Return type:
Digraph- Returns:
graphviz.Digraph object (call .render() to save)
- Raises:
ImportError – If graphviz is not installed
Void of Course Moon calculation engine.
The Moon is void of course (VOC) when it will not complete any major (Ptolemaic) aspects before leaving its current sign. This is traditionally considered an inauspicious time for beginning new ventures.
Traditional VOC uses only the seven visible planets (Sun through Saturn). Modern VOC includes the outer planets (Uranus, Neptune, Pluto).
Example
>>> result = chart.voc_moon()
>>> if result.is_void:
... print(f"Moon is VOC until {result.void_until}")
... else:
... print(f"Moon will {result.next_aspect} before leaving {result.moon_sign}")
- class stellium.engines.voc.VOCMoonResult(is_void, moon_longitude, moon_sign, void_until, ends_by, next_aspect, next_aspect_degree, next_planet, next_sign, ingress_time, aspect_mode)[source]
Bases:
objectResult of a void-of-course Moon calculation.
- is_void
True if the Moon is currently void of course
- moon_longitude
Current Moon longitude in degrees
- moon_sign
Current zodiac sign of the Moon
- void_until
Datetime when the void period ends
- ends_by
How the void ends - “aspect” or “ingress”
- next_aspect
Description of next aspect (e.g., “trine Jupiter”) or None
- next_aspect_degree
The aspect angle (0, 60, 90, 120, 180) or None
- next_planet
Name of planet Moon will next aspect, or None
- next_sign
The sign Moon will enter next
- ingress_time
Datetime when Moon enters the next sign
- aspect_mode
Which planet set was used (“traditional” or “modern”)
- aspect_mode: str
- ends_by: Literal['aspect', 'ingress']
- ingress_time: datetime
- is_void: bool
- moon_longitude: float
- moon_sign: str
- next_sign: str
- void_until: datetime
- stellium.engines.voc.calculate_voc_moon(chart, aspects='traditional')[source]
Calculate void-of-course Moon status for a chart.
The Moon is void of course when it will not perfect any major Ptolemaic aspect (conjunction, sextile, square, trine, opposition) to any planet before leaving its current sign.
- Parameters:
chart (
CalculatedChart) – The calculated chart to analyzeaspects (
Literal['traditional','modern']) – “traditional” (Sun-Saturn) or “modern” (includes outers)
- Return type:
VOCMoonResult- Returns:
VOCMoonResult with void status and timing details
- Raises:
ValueError – If Moon is not found in chart
Longitude search engine for finding when celestial objects reach specific positions.
This module provides efficient search functions for finding exact times when planets and other celestial objects cross specific zodiac degrees. Uses a hybrid Newton-Raphson / bisection algorithm for fast, reliable convergence.
Key features: - Fast convergence using planetary speed from Swiss Ephemeris - Handles retrograde motion and stations gracefully - Proper 360°/0° wraparound handling - Forward and backward search directions - Find single crossing or all crossings in a date range
- class stellium.engines.search.AngleCrossing(julian_day, datetime_utc, angle_name, target_longitude, actual_longitude, latitude, longitude)[source]
Bases:
objectResult of an angle crossing search.
Represents the moment when a chart angle (ASC, MC, DSC, IC) reaches a specific zodiac longitude.
- julian_day
Julian day when angle reaches the longitude
- datetime_utc
UTC datetime of the crossing
- angle_name
Which angle (“ASC”, “MC”, “DSC”, “IC”)
- target_longitude
The target longitude that was crossed
- actual_longitude
The actual angle longitude at crossing
- latitude
Geographic latitude used
- longitude
Geographic longitude used
- actual_longitude: float
- angle_name: str
- datetime_utc: datetime
- julian_day: float
- latitude: float
- longitude: float
- target_longitude: float
- class stellium.engines.search.AspectExact(julian_day, datetime_utc, object1_name, object2_name, aspect_angle, aspect_name, object1_longitude, object2_longitude, is_applying_before)[source]
Bases:
objectResult of an aspect exactitude search.
Represents the exact moment when two celestial objects form a precise aspect (conjunction, sextile, square, trine, or opposition).
- julian_day
Julian day when aspect is exact
- datetime_utc
UTC datetime when aspect is exact
- object1_name
First object name
- object2_name
Second object name
- aspect_angle
The aspect angle (0=conjunction, 60=sextile, etc.)
- aspect_name
Human name (“conjunction”, “trine”, etc.)
- object1_longitude
First object’s longitude at exact
- object2_longitude
Second object’s longitude at exact
- is_applying_before
True if aspect was applying before exact
- aspect_angle: float
- aspect_name: str
- datetime_utc: datetime
- is_applying_before: bool
- julian_day: float
- object1_longitude: float
- object1_name: str
- object2_longitude: float
- object2_name: str
- property separation: float
Angular separation between the two objects (should be ~aspect_angle).
- class stellium.engines.search.Eclipse(julian_day, datetime_utc, eclipse_type, sun_longitude, moon_longitude, node_longitude, orb_to_node, nearest_node, sign, classification)[source]
Bases:
objectResult of an eclipse search.
- julian_day
Julian day of the eclipse (exact Sun-Moon conjunction/opposition)
- datetime_utc
UTC datetime of the eclipse
- eclipse_type
“solar” or “lunar”
- sun_longitude
Sun’s longitude at eclipse
- moon_longitude
Moon’s longitude at eclipse
- node_longitude
True Node longitude at eclipse
- orb_to_node
Distance from Sun/Moon to nearest node (degrees)
- nearest_node
Which node is involved (“north” or “south”)
- sign
Zodiac sign of the eclipse
- classification
“total”, “annular”, “partial”, or “penumbral”
- classification: str
- datetime_utc: datetime
- property degree_in_sign: float
Degree within the sign (0-30).
- eclipse_type: Literal['solar', 'lunar']
- property is_lunar: bool
True if this is a lunar eclipse.
- property is_solar: bool
True if this is a solar eclipse.
- julian_day: float
- moon_longitude: float
- nearest_node: Literal['north', 'south']
- node_longitude: float
- orb_to_node: float
- sign: str
- sun_longitude: float
- class stellium.engines.search.LongitudeCrossing(julian_day, datetime_utc, longitude, speed, is_retrograde, object_name)[source]
Bases:
objectResult of a longitude search.
- julian_day
Julian day of the crossing
- datetime_utc
UTC datetime of the crossing
- longitude
Exact longitude at crossing (should be very close to target)
- speed
Longitude speed at crossing (degrees/day, negative = retrograde)
- is_retrograde
True if object was moving retrograde at crossing
- object_name
Name of the celestial object
- datetime_utc: datetime
- property is_direct: bool
True if object was moving direct (not retrograde) at crossing.
- is_retrograde: bool
- julian_day: float
- longitude: float
- object_name: str
- speed: float
- class stellium.engines.search.SignIngress(julian_day, datetime_utc, object_name, sign, from_sign, longitude, speed, is_retrograde)[source]
Bases:
objectResult of a sign ingress search.
- julian_day
Julian day of the ingress
- datetime_utc
UTC datetime of the ingress
- object_name
Name of the celestial object
- sign
Sign being entered
- from_sign
Sign being left
- longitude
Exact longitude at ingress (should be very close to 0° of sign)
- speed
Longitude speed at ingress (degrees/day, negative = retrograde)
- is_retrograde
True if object was moving retrograde at ingress
- datetime_utc: datetime
- from_sign: str
- property is_direct: bool
True if object was moving direct (not retrograde) at ingress.
- is_retrograde: bool
- julian_day: float
- longitude: float
- object_name: str
- sign: str
- speed: float
- class stellium.engines.search.Station(julian_day, datetime_utc, object_name, station_type, longitude, sign)[source]
Bases:
objectResult of a planetary station search.
A station occurs when a planet’s apparent motion changes direction - either from direct to retrograde, or retrograde to direct.
- julian_day
Julian day of the station
- datetime_utc
UTC datetime of the station
- object_name
Name of the celestial object
- station_type
“retrograde” (turning Rx) or “direct” (turning D)
- longitude
Longitude at the station (planet “hovers” here)
- sign
Zodiac sign of the station
- datetime_utc: datetime
- property degree_in_sign: float
Degree within the sign (0-30).
- property is_turning_direct: bool
True if planet is turning direct (was Rx, now direct).
- property is_turning_retrograde: bool
True if planet is turning retrograde (was direct, now Rx).
- julian_day: float
- longitude: float
- object_name: str
- sign: str
- station_type: Literal['retrograde', 'direct']
- stellium.engines.search.find_all_angle_crossings(target_longitude, latitude, longitude, angle, start, end, max_results=100)[source]
Find all times a chart angle crosses a specific longitude in a date range.
- Parameters:
target_longitude (
float) – Target longitude in degrees (0-360)latitude (
float) – Geographic latitudelongitude (
float) – Geographic longitude (negative = West)angle (
str) – Which angle to track (“ASC”, “MC”, “DSC”, “IC”)start (
datetime|float) – Start datetime (UTC) or Julian daymax_results (
int) – Safety limit on number of results
- Return type:
list[AngleCrossing]- Returns:
List of AngleCrossing objects, chronologically ordered
Example
>>> # Find all times ASC crosses 0° Aries in January 2025 >>> results = find_all_angle_crossings( ... 0.0, 40.7, -74.0, "ASC", ... datetime(2025, 1, 1), datetime(2025, 2, 1) ... ) >>> print(f"Found {len(results)} crossings") # ~31 (once per day)
- stellium.engines.search.find_all_aspect_exacts(object1_name, object2_name, aspect_angle, start, end, max_results=100)[source]
Find all exact aspects between two objects in a date range.
Useful for: - Finding all Moon-Jupiter trines in a year - Tracking Mercury-Venus aspects for relationship timing - Building aspect timelines
- Parameters:
object1_name (
str) – First object (e.g., “Moon”, “Sun”, “Mars”)object2_name (
str) – Second object (e.g., “Jupiter”, “Venus”)aspect_angle (
float) – Target angle in degrees (0, 60, 90, 120, 180)start (
datetime|float) – Start datetime (UTC) or Julian daymax_results (
int) – Safety limit on number of results (default 100)
- Return type:
list[AspectExact]- Returns:
List of AspectExact objects, chronologically ordered
Example
>>> # Find all Moon trine Jupiter in 2024 >>> results = find_all_aspect_exacts( ... "Moon", "Jupiter", 120.0, ... datetime(2024, 1, 1), ... datetime(2024, 12, 31) ... ) >>> print(f"Found {len(results)} exact trines") >>> for r in results[:5]: ... print(r)
- stellium.engines.search.find_all_eclipses(start, end, eclipse_types='both', max_results=20)[source]
Find all eclipses in a date range.
- Parameters:
- Return type:
list[Eclipse]- Returns:
List of Eclipse objects, chronologically ordered
Example
>>> # Find all eclipses in 2024 >>> eclipses = find_all_eclipses( ... datetime(2024, 1, 1), ... datetime(2024, 12, 31) ... ) >>> for e in eclipses: ... print(e)
- stellium.engines.search.find_all_ingresses(object_name, sign, start, end, max_results=50)[source]
Find all times a celestial object enters a specific sign in a date range.
- Parameters:
- Return type:
list[SignIngress]- Returns:
List of SignIngress objects, chronologically ordered
Example
>>> # Find all Mars ingresses to Aries in the next 10 years >>> results = find_all_ingresses( ... "Mars", "Aries", ... datetime(2024, 1, 1), ... datetime(2034, 1, 1) ... ) >>> print(f"Mars enters Aries {len(results)} times")
- stellium.engines.search.find_all_longitude_crossings(object_name, target_longitude, start, end, max_results=100)[source]
Find all times a celestial object crosses a specific longitude in a date range.
Useful for: - Finding all Moon transits over a degree (roughly monthly) - Finding multiple Mercury crossings during retrograde (up to 3) - Building transit timelines
- Parameters:
- Return type:
list[LongitudeCrossing]- Returns:
List of LongitudeCrossing objects, chronologically ordered
Example
>>> # Find all times Moon crosses 15° Taurus in 2024 >>> results = find_all_longitude_crossings( ... "Moon", 45.0, # 15° Taurus ... datetime(2024, 1, 1), ... datetime(2024, 12, 31) ... ) >>> print(f"Moon crosses 15° Taurus {len(results)} times in 2024")
- stellium.engines.search.find_all_sign_changes(object_name, start, end, max_results=100)[source]
Find all sign changes for a celestial object in a date range.
- Parameters:
- Return type:
list[SignIngress]- Returns:
List of SignIngress objects, chronologically ordered
Example
>>> # Find all Mercury sign changes in 2024 >>> results = find_all_sign_changes( ... "Mercury", ... datetime(2024, 1, 1), ... datetime(2024, 12, 31) ... ) >>> for r in results: ... print(f"{r.datetime_utc.date()}: Mercury enters {r.sign}")
- stellium.engines.search.find_all_stations(object_name, start, end, max_results=50)[source]
Find all planetary stations in a date range.
- Parameters:
- Return type:
list[Station]- Returns:
List of Station objects, chronologically ordered
Example
>>> # Find all Mercury stations in 2024 >>> results = find_all_stations( ... "Mercury", ... datetime(2024, 1, 1), ... datetime(2024, 12, 31) ... ) >>> for r in results: ... print(r)
- stellium.engines.search.find_angle_crossing(target_longitude, latitude, longitude, angle, start, direction='forward', max_days=2.0, tolerance=0.001, max_iterations=50)[source]
Find when a chart angle crosses a specific longitude.
Since angles rotate with Earth’s rotation (~1° every 4 minutes), any given longitude will be crossed roughly once per sidereal day (~23h 56m) by each angle.
- Parameters:
target_longitude (
float) – Target longitude in degrees (0-360)latitude (
float) – Geographic latitudelongitude (
float) – Geographic longitude (negative = West)angle (
str) – Which angle to track (“ASC”, “MC”, “DSC”, “IC”)start (
datetime|float) – Starting datetime (UTC) or Julian daydirection (
Literal['forward','backward']) – “forward” to search future, “backward” to search pastmax_days (
float) – Maximum days to search (default 2, since crossings are daily)tolerance (
float) – Convergence tolerance in degreesmax_iterations (
int) – Maximum refinement iterations
- Return type:
AngleCrossing|None- Returns:
AngleCrossing with exact timing, or None if not found
Example
>>> # Find when ASC reaches 0° Leo in NYC >>> result = find_angle_crossing(120.0, 40.7, -74.0, "ASC", datetime.now()) >>> print(f"ASC at 0° Leo: {result.datetime_utc}")
- stellium.engines.search.find_aspect_exact(object1_name, object2_name, aspect_angle, start, direction='forward', max_days=366.0, tolerance=0.0001, max_iterations=50)[source]
Find when two objects form an exact aspect.
Uses a hybrid Newton-Raphson / bisection algorithm to find the precise moment when two celestial objects reach a specific angular separation.
- Parameters:
object1_name (
str) – First object (e.g., “Moon”, “Sun”, “Mars”)object2_name (
str) – Second object (e.g., “Jupiter”, “Venus”)aspect_angle (
float) – Target angle in degrees (0=conjunction, 60=sextile, 90=square, 120=trine, 180=opposition)start (
datetime|float) – Starting datetime (UTC) or Julian daydirection (
Literal['forward','backward']) – “forward” to search future, “backward” to search pastmax_days (
float) – Maximum days to search (default 366 = just over a year)tolerance (
float) – Convergence tolerance in degrees (default 0.0001 ≈ 0.36 arcsec)max_iterations (
int) – Maximum refinement iterations
- Return type:
AspectExact|None- Returns:
AspectExact with exact timing, or None if not found
Example
>>> # Find next exact Moon trine Jupiter >>> result = find_aspect_exact("Moon", "Jupiter", 120.0, datetime(2024, 1, 1)) >>> print(f"Exact trine at {result.datetime_utc}")
>>> # Find next Mercury-Venus conjunction >>> result = find_aspect_exact("Mercury", "Venus", 0.0, datetime(2024, 1, 1)) >>> print(result)
- stellium.engines.search.find_eclipse(start, direction='forward', eclipse_types='both', max_days=200.0)[source]
Find the next eclipse from a starting date.
- Parameters:
start (
datetime|float) – Starting datetime (UTC) or Julian daydirection (
Literal['forward','backward']) – “forward” to search future, “backward” to search pasteclipse_types (
Literal['both','solar','lunar']) – Which types to find (“both”, “solar”, “lunar”)max_days (
float) – Maximum days to search (default 200 = ~6 months)
- Return type:
Eclipse|None- Returns:
Eclipse with details, or None if not found
Example
>>> # Find next eclipse from now >>> eclipse = find_eclipse(datetime(2024, 1, 1)) >>> print(eclipse) Partial lunar eclipse at 5°07' Leo (NN) on 2024-03-25 07:00
- stellium.engines.search.find_ingress(object_name, sign, start, direction='forward', max_days=730.0)[source]
Find when a celestial object next enters a specific zodiac sign.
- Parameters:
object_name (
str) – Name of celestial object (e.g., “Sun”, “Mars”, “Moon”)sign (
str) – Target zodiac sign (e.g., “Aries”, “Taurus”)start (
datetime|float) – Starting datetime (UTC) or Julian daydirection (
Literal['forward','backward']) – “forward” to search future, “backward” to search pastmax_days (
float) – Maximum days to search (default 730 = ~2 years)
- Return type:
SignIngress|None- Returns:
SignIngress with exact time of ingress, or None if not found
Example
>>> # When does Mars next enter Aries? >>> result = find_ingress("Mars", "Aries", datetime(2024, 1, 1)) >>> print(result) # Mars enters Aries on 2024-04-30 12:34
- stellium.engines.search.find_longitude_crossing(object_name, target_longitude, start, direction='forward', max_days=366.0, tolerance=0.0001, max_iterations=50)[source]
Find when a celestial object crosses a specific longitude.
Uses a hybrid Newton-Raphson / bisection algorithm: 1. First brackets the crossing with a coarse sweep 2. Then refines with Newton-Raphson (fast when speed is good) 3. Falls back to bisection near stations (speed ≈ 0)
- Parameters:
object_name (
str) – Name of celestial object (e.g., “Sun”, “Mars”, “Moon”)target_longitude (
float) – Target longitude in degrees (0-360)start (
datetime|float) – Starting datetime (UTC) or Julian daydirection (
Literal['forward','backward']) – “forward” to search future, “backward” to search pastmax_days (
float) – Maximum days to search (default 366 = just over a year)tolerance (
float) – Convergence tolerance in degrees (default 0.0001 ≈ 0.36 arcsec)max_iterations (
int) – Maximum refinement iterations
- Return type:
LongitudeCrossing|None- Returns:
LongitudeCrossing with exact time, or None if not found
Example
>>> # When does the Sun reach 0° Aries (vernal equinox) after Jan 1, 2024? >>> result = find_longitude_crossing("Sun", 0.0, datetime(2024, 1, 1)) >>> print(result.datetime_utc) # ~March 20, 2024
- stellium.engines.search.find_next_sign_change(object_name, start, direction='forward', max_days=60.0)[source]
Find when a celestial object next changes signs (enters any sign).
This is useful for questions like “when does this transit end?” where you don’t care which sign is entered, just when the object leaves its current sign.
- Parameters:
- Return type:
SignIngress|None- Returns:
SignIngress with exact time of sign change, or None if not found
Example
>>> # When does Mars leave its current sign? >>> result = find_next_sign_change("Mars", datetime(2024, 1, 15)) >>> print(f"Mars enters {result.sign} on {result.datetime_utc}")
- stellium.engines.search.find_station(object_name, start, direction='forward', max_days=400.0)[source]
Find the next planetary station (retrograde or direct).
A station occurs when a planet appears to stop moving and reverse direction. This is an important timing point in astrology.
Note: Sun and Moon do not have stations (they don’t go retrograde).
- Parameters:
object_name (
str) – Name of celestial object (e.g., “Mercury”, “Mars”)start (
datetime|float) – Starting datetime (UTC) or Julian daydirection (
Literal['forward','backward']) – “forward” to search future, “backward” to search pastmax_days (
float) – Maximum days to search (default 400, > 1 year for outer planets)
- Return type:
Station|None- Returns:
Station with exact time and details, or None if not found
Example
>>> # When does Mercury next station? >>> result = find_station("Mercury", datetime(2024, 1, 1)) >>> print(result) # Mercury stations retrograde at 4°51' Capricorn on 2024-04-01
Primary directions calculation engine.
This module provides primary directions calculations for Stellium, supporting both zodiacal (2D/Regiomontanus-style) and mundane (3D/Placidus) methods.
Primary directions track when a “promissor” (moving point) reaches a “significator” (target point) via the Earth’s daily rotation. The resulting arc is converted to years using a time key.
Example
>>> from stellium.engines.directions import DirectionsEngine
>>> engine = DirectionsEngine(chart)
>>> result = engine.direct("Sun", "ASC")
>>> print(f"Sun to ASC: age {result.age:.1f}")
- class stellium.engines.directions.DirectionArc(promissor, significator, arc_degrees, method, direction='direct')[source]
Bases:
objectThe result of a primary direction calculation.
- promissor
Name of the moving point
- significator
Name of the target point
- arc_degrees
The calculated arc in degrees
- method
Direction method used (“zodiacal” or “mundane”)
- direction
“direct” or “converse”
- arc_degrees: float
- direction: str = 'direct'
- method: str
- promissor: str
- significator: str
- class stellium.engines.directions.DirectionMethod(*args, **kwargs)[source]
Bases:
ProtocolProtocol for direction calculation methods.
Different implementations provide different mathematical approaches: - ZodiacalDirections: Projects onto ecliptic plane (2D) - MundaneDirections: Uses house-space proportions (3D/Placidus)
- calculate_arc(promissor, significator, latitude, ramc)[source]
Calculate the arc between promissor and significator.
- property method_name: str
Name of this direction method.
- class stellium.engines.directions.DirectionResult(arc, date=None, age=None)[source]
Bases:
objectComplete result of directing one point to another.
- arc
The direction arc details
- date
Calendar date when the direction perfects (if calculable)
- age
Age in years when the direction perfects (if calculable)
- arc: DirectionArc
- class stellium.engines.directions.DirectionsEngine(chart, method='zodiacal', time_key='naibod')[source]
Bases:
objectPrimary directions calculation engine.
Calculates primary directions between chart points using either zodiacal (2D) or mundane (3D/Placidus) methods.
- Parameters:
chart (
CalculatedChart) – The natal chart to calculate directions formethod (
str) – Direction method - “zodiacal” (default) or “mundane”time_key (
str) – Time key - “naibod” (default) or “ptolemy”
Example
>>> engine = DirectionsEngine(chart) >>> result = engine.direct("Sun", "ASC") >>> print(f"Sun to ASC: age {result.age:.1f}")
>>> # Compare methods >>> z = DirectionsEngine(chart, method="zodiacal").direct("Sun", "ASC") >>> m = DirectionsEngine(chart, method="mundane").direct("Sun", "ASC")
- METHODS: dict[str, type[DirectionMethod]] = {'mundane': <class 'stellium.engines.directions.MundaneDirections'>, 'zodiacal': <class 'stellium.engines.directions.ZodiacalDirections'>}
- TIME_KEYS: dict[str, type[TimeKey]] = {'naibod': <class 'stellium.engines.directions.NaibodKey'>, 'ptolemy': <class 'stellium.engines.directions.PtolemyKey'>}
- direct(promissor, significator, direction='direct')[source]
Calculate a primary direction.
- Parameters:
promissor (
str|CelestialPosition) – Name or position of moving point (e.g., “Sun”)significator (
str|CelestialPosition) – Name or position of target (e.g., “ASC”)direction (
str) – “direct” or “converse”
- Return type:
DirectionResult- Returns:
DirectionResult with arc, date, and age
- direct_all_to(significator, planets=None)[source]
Direct multiple planets to a single significator.
- direct_to_angles(promissor)[source]
Direct a planet to all four angles.
- Parameters:
promissor (
str|CelestialPosition) – Name or position of the planet- Return type:
- Returns:
Dictionary mapping angle names to DirectionResults
- class stellium.engines.directions.DistributionsCalculator(chart, time_key='naibod', bound_system='egypt')[source]
Bases:
objectCalculate term/bound distributions.
Distributions track which planetary term (bound) the directed Ascendant occupies at different ages. This creates a timeline of “life chapters” ruled by different planets.
- Parameters:
chart (
CalculatedChart) – The natal charttime_key (
str) – Time key - “naibod” (default) or “ptolemy”bound_system (
str) – Which bound system to use (default: “egypt”)
Example
>>> calc = DistributionsCalculator(chart) >>> periods = calc.calculate(years=80) >>> for p in periods: ... print(f"Age {p.start_age:.1f}: {p.ruler} ({p.sign})")
- class stellium.engines.directions.EquatorialPoint(name, right_ascension, declination)[source]
Bases:
objectA point in equatorial coordinates (RA/Dec).
This is the universal coordinate system for primary directions. All chart positions are converted to this format before calculation.
- name
Name of the point (e.g., “Sun”, “ASC”)
- right_ascension
Right ascension in degrees (0-360)
- declination
Declination in degrees (-90 to +90)
- declination: float
- name: str
- right_ascension: float
- class stellium.engines.directions.MundaneDirections[source]
Bases:
objectMundane (Placidus) primary directions.
Uses house-space proportions. The promissor must travel to reach the same “mundane ratio” as the significator. This is the “3D” method.
The mundane ratio is how far through its current semi-arc a point has traveled (0 = at meridian, 1 = at horizon).
- calculate_arc(promissor, significator, latitude, ramc)[source]
Calculate mundane arc using Placidus proportions.
The promissor must travel until it reaches the same proportional position within its semi-arc as the significator.
- Return type:
- property method_name: str
- class stellium.engines.directions.MundanePosition(point, meridian_distance, semi_arc_diurnal, semi_arc_nocturnal, is_above_horizon, is_eastern)[source]
Bases:
objectA point with full mundane (house-space) context.
Knows its position relative to the local horizon/meridian system. Used by the MundaneDirections method for Placidus-style calculations.
- point
The underlying equatorial point
- meridian_distance
Degrees from MC (-180 to +180, positive = east)
- semi_arc_diurnal
Degrees of the day arc radius
- semi_arc_nocturnal
Degrees of the night arc radius
- is_above_horizon
True if point is above the horizon
- is_eastern
True if point is on the rising (eastern) side
- property current_semi_arc: float
Which semi-arc is currently applicable.
- is_above_horizon: bool
- is_eastern: bool
- meridian_distance: float
- property mundane_ratio: float
Position as fraction of semi-arc (0=meridian, 1=horizon).
- point: EquatorialPoint
- semi_arc_diurnal: float
- semi_arc_nocturnal: float
- class stellium.engines.directions.NaibodKey[source]
Bases:
objectThe Precision Key: Based on mean solar motion.
Uses the Sun’s mean daily motion of 59’08” (0.9856 deg per day). This makes 1 deg approx 1.0146 years.
- DAYS_PER_DEGREE = 370.5765625
- property key_name: str
- class stellium.engines.directions.PtolemyKey[source]
Bases:
objectThe Classic Key: 1 degree = 1 year.
The simplest and oldest time key, attributed to Ptolemy.
- arc_to_date(arc, birth_date)[source]
Convert arc to date using 1 deg = 1 year.
- Return type:
- property key_name: str
- class stellium.engines.directions.TermBoundary(absolute_degree, ruler, sign)[source]
Bases:
objectRepresents the starting boundary of a term.
- absolute_degree
Position in the zodiac (0-360)
- ruler
Planet ruling this term
- sign
Zodiac sign name
- absolute_degree: float
- ruler: str
- sign: str
- class stellium.engines.directions.TimeKey(*args, **kwargs)[source]
Bases:
ProtocolProtocol for converting arcs to time.
Different keys represent different symbolic rates of motion: - Ptolemy: 1 degree = 1 year - Naibod: Based on mean solar motion (~1.0146 years/degree)
- property key_name: str
Name of this time key.
- class stellium.engines.directions.TimeLordPeriod(ruler, start_date, start_age, sign='', end_date=None, end_age=None)[source]
Bases:
objectA period ruled by a term/bound lord.
Used in distributions to track which planetary term the directed Ascendant occupies at different ages.
- ruler
Name of the ruling planet
- start_date
Date this period begins
- start_age
Age when this period begins
- sign
Zodiac sign containing this term
- end_date
Date this period ends (optional)
- end_age
Age when this period ends (optional)
- ruler: str
- sign: str = ''
- start_age: float
- start_date: datetime
- class stellium.engines.directions.ZodiacalDirections[source]
Bases:
objectZodiacal (Regiomontanus-style) primary directions.
Projects points onto the ecliptic plane. The significator’s pole determines the projection plane. This is the “2D” method.
In zodiacal directions, we compare oblique ascensions calculated using the same pole (typically the geographic latitude for ASC).
- calculate_arc(promissor, significator, latitude, ramc)[source]
Calculate zodiacal arc via oblique ascension difference.
The arc is the difference in oblique ascension between the promissor and significator, using the same pole for both.
- Return type:
- property method_name: str
- stellium.engines.directions.ascensional_difference(declination, pole)[source]
Calculate ascensional difference (the ‘wobble’ from pole tilt).
The ascensional difference is how much a point’s rising/setting time deviates from 6 hours due to both its own declination and the observer’s latitude (pole).
Formula: sin(AD) = tan(dec) * tan(pole)
- stellium.engines.directions.ecliptic_to_equatorial(longitude, latitude, obliquity)[source]
Convert ecliptic coordinates to equatorial.
- stellium.engines.directions.get_obliquity(julian_day)[source]
Get the true obliquity of the ecliptic for a given time.
- stellium.engines.directions.meridian_distance(right_ascension, ramc)[source]
Calculate distance from the upper meridian (MC).
Positive values indicate the point is east (rising toward MC). Negative values indicate the point is west (setting from MC).
- stellium.engines.directions.oblique_ascension(right_ascension, declination, pole)[source]
Calculate oblique ascension.
Oblique ascension is the RA adjusted for the pole (geographic latitude or house pole). It’s used in zodiacal directions.
Formula: OA = RA - AD
- stellium.engines.directions.semi_arcs(declination, latitude)[source]
Calculate diurnal and nocturnal semi-arcs.
A semi-arc is half the arc a point travels above (diurnal) or below (nocturnal) the horizon. At the equator with 0 declination, both are 90.
Implementation of standard Zodiacal Releasing system.
- class stellium.engines.releasing.ZodiacalReleasingAnalyzer(lots, engine=<class 'stellium.engines.releasing.ZodiacalReleasingEngine'>, max_level=4, lifespan=100)[source]
Bases:
objectCalculate Zodiacal Releasing timeline and periods.
- analyze(chart)[source]
Add zodiacial releasing timeline to metadata.
- Parameters:
chart (
CalculatedChart) – Chart to analyze- Returns:
ZRTimeline}
- Return type:
Dict of {lot name
- property analyzer_name: str
- property metadata_name: str
Components (stellium.components)¶
Optional calculation components that can be added to charts.
Arabic Parts calculator component.
Arabic Parts (also called Lots) are calculated points based on the distances between three chart objects. They represent themes or areas of life.
Formula: Lot = Asc + Point2 - Point1
Many lots are “sect-aware” - they flip the formula for day vs night charts: - Day Chart: Asc + Point2 - Point1 - Night Chart: Asc + Point1 - Point2
- class stellium.components.arabic_parts.ArabicPartsCalculator(parts_to_calculate=None, custom_parts=None)[source]
Bases:
objectCalculate Arabic Parts (Lots) for a chart.
Arabic Parts are senstitive points calculated from the distances between three chart objects. They represent specific life themes.
- calculate(datetime, location, positions, house_systems_map, house_placements_map)[source]
Calculate Arabic Parts.
- Parameters:
datetime (
ChartDateTime) – Chart datetime (unused, required by protocol)location (
ChartLocation) – Chart location (unused, required by protocol)positions (
list[CelestialPosition]) – Already-calculated positionshouse_systems_map (
dict[str,HouseCusps]) – House systems and House cuspshouse_placements_map (
dict[str,dict[str,int]]) – Object placement by house system
- Return type:
- Returns:
List of CelestialPosition objects for each part
- property component_name: str
- class stellium.components.arabic_parts.PartOfFortuneCalculator[source]
Bases:
objectSimplified calculator for just Part of Fortune.
This is useful for when you only need Fortune and don’t want to calculate all Arabic Parts.
- calculate(datetime, location, positions, house_systems_map, house_placements_map)[source]
Calculate only Part of Fortune.
- Return type:
- property component_name: str
Midpoint calculator component.
Midpoints are the halfway point between two celestial objects. They represent the synthesis or blend of two planetary energies.
In midpoint astrology: - Direct midpoint: Shortest arc between two points - Indirect midpoint: Opposite point (180° from direct)
Both are significant, but direct midpoint is more commonly used.
- class stellium.components.midpoints.MidpointCalculator(pairs=None, calculate_all=False, indirect=False)[source]
Bases:
objectCalculate midpoints between celestial objects.
Midpoints reveal how two planetary energies blend or interact. They’re used extensively in Uranian astrology and some modern approaches.
- DEFAULT_PAIRS = [('Sun', 'Moon'), ('Sun', 'ASC'), ('Sun', 'MC'), ('Moon', 'ASC'), ('Moon', 'MC'), ('ASC', 'MC'), ('Sun', 'Mercury'), ('Sun', 'Venus'), ('Sun', 'Mars'), ('Moon', 'Mercury'), ('Moon', 'Venus'), ('Moon', 'Mars'), ('Mercury', 'Venus'), ('Mercury', 'Mars'), ('Venus', 'Mars'), ('Sun', 'Jupiter'), ('Sun', 'Saturn'), ('Moon', 'Jupiter'), ('Moon', 'Saturn'), ('Mars', 'Jupiter'), ('Mars', 'Saturn'), ('Venus', 'Jupiter'), ('Jupiter', 'Saturn')]
- calculate(datetime, location, positions, house_systems_map, house_placements_map)[source]
Calculate midpoints.
- Parameters:
datetime (
ChartDateTime) – Chart datetime (unused)location (
ChartLocation) – Chart location (unused)positions (
list[CelestialPosition]) – Already calculated positionshouse_systems_map (
dict[str,HouseCusps]) – House cusps for house assignment (unused)
- Return type:
- Returns:
List of CelestialPosition objects for midpoints
- property component_name: str
Dignity component for ChartBuilder.
Calculates essential and accidental dignities for all planets in a chart. Integrates seamlessly with the ChartBuilder component system.
- class stellium.components.dignity.AccidentalDignityComponent(house_system='Placidus')[source]
Bases:
objectChart component that calculates accidental dignities (house placement, etc).
This should be added AFTER house systems are calculated.
Handles multiple house systems by calculating house-dependent conditions for each system separately, while universal conditions (retrograde, cazimi, etc.) are calculated once.
- Usage:
chart = ChartBuilder.from_native(native) .add_component(AccidentalDignityComponent()) .calculate()
# Access for specific system sun_acc = chart.get_planet_accidental(“Sun”, system=”Placidus”)
# Or get all systems sun_all = chart.get_planet_accidental(“Sun”)
- ANGULAR_HOUSES = [1, 10, 7, 4]
- CADENT_HOUSES = [3, 12, 9, 6]
- JOY_PLACEMENTS = {'Jupiter': 11, 'Mars': 6, 'Mercury': 1, 'Moon': 3, 'Saturn': 12, 'Sun': 9, 'Venus': 5}
- SUCCEDENT_HOUSES = [2, 11, 8, 5]
- calculate(datetime, location, positions, house_systems_map, house_placements_map)[source]
Calculate accidental dignities based on house position.
- Parameters:
datetime (
ChartDateTime) – Chart datetimelocation (
ChartLocation) – Chart locationpositions (
list[CelestialPosition]) – Already calculated positionshouse_systems_map (
dict[str,HouseCusps]) – All calculated house systems
- Return type:
- Returns:
Empty list (accidental dignities stored in metadata)
- property component_name: str
Name of this component.
- metadata_name = 'accidental_dignities'
- class stellium.components.dignity.DignityComponent(traditional=True, modern=True, receptions=True, decans='triplicity')[source]
Bases:
objectChart component that calculates dignities for all planets.
This follows the ChartComponent protocol and can be added to ChartBuilder like any other component.
- Usage:
chart = ChartBuilder.from_native(native) .add_component(DignityComponent()) .calculate()
# Access dignities dignities = chart.metadata.get(‘dignities’, {})
- calculate(datetime, location, positions, house_systems_map, house_placements_map)[source]
Calculate dignities for all positions.
Note: This component doesn’t return new positions - instead it returns an empty list and stores dignity data in metadata that will be attached to the CalculatedChart.
- Parameters:
datetime (
ChartDateTime) – Chart datetimelocation (
ChartLocation) – Chart locationpositions (
list[CelestialPosition]) – Already calculated positionshouse_systems_map (
dict[str,HouseCusps]) – All calculated house systemshouse_placements_map (
dict[str,dict[str,int]]) – House placements for all systems
- Return type:
- Returns:
Empty list (dignities stored in metadata)
- property component_name: str
Name of this component.
- get_metadata()[source]
Get calculated dignity data.
This should be called after calculate() to retrieve results.
- metadata_name = 'dignities'
- stellium.components.dignity.determine_sect(positions)[source]
Determines if a day or night chart. Returns ‘day’ or ‘night.’
Day chart = Sun above the horizon, between ASC and DSC (going through MC).
- Return type:
Fixed Stars component for chart calculations.
This component calculates fixed star positions and integrates them into the chart. Stars are calculated for the chart’s Julian Day, with Swiss Ephemeris handling precession automatically.
- Usage:
>>> from stellium import ChartBuilder >>> from stellium.components import FixedStarsComponent >>> >>> # All stars >>> chart = (ChartBuilder.from_native(native) ... .add_component(FixedStarsComponent()) ... .calculate()) >>> >>> # Royal stars only >>> chart = (ChartBuilder.from_native(native) ... .add_component(FixedStarsComponent(tier=1)) ... .calculate()) >>> >>> # Specific stars >>> chart = (ChartBuilder.from_native(native) ... .add_component(FixedStarsComponent(stars=["Regulus", "Algol"])) ... .calculate())
- class stellium.components.fixed_stars.FixedStarsComponent(stars=None, tier=None, royal_only=False, include_higher_tiers=False)[source]
Bases:
objectComponent that calculates fixed star positions for a chart.
This component uses SwissEphemerisFixedStarsEngine to calculate precise positions for fixed stars and returns them as FixedStarPosition objects.
Stars can be filtered by: - Specific star names - Tier (1=Royal, 2=Major, 3=Extended) - Royal stars only
The calculated positions include all metadata from FIXED_STARS_REGISTRY, including traditional planetary nature, keywords, and constellation.
- stars
List of specific star names to calculate (None = use tier/royal filters)
- tier
Tier level to calculate (None = all tiers)
- royal_only
If True, calculate only the four Royal Stars
Example
>>> # Add all stars in registry >>> comp = FixedStarsComponent() >>> >>> # Add only Royal stars (Aldebaran, Regulus, Antares, Fomalhaut) >>> comp = FixedStarsComponent(royal_only=True) >>> >>> # Add only tier 1 and 2 stars >>> comp = FixedStarsComponent(tier=2, include_higher_tiers=True) >>> >>> # Add specific stars >>> comp = FixedStarsComponent(stars=["Sirius", "Algol", "Spica"])
- calculate(datetime, location, positions, house_systems_map, house_placements_map)[source]
Calculate fixed star positions for the chart.
- Parameters:
datetime (
ChartDateTime) – Chart datetime (used to get Julian Day)location (
ChartLocation) – Chart location (unused for fixed stars, required by protocol)positions (
list[CelestialPosition]) – Already-calculated positions (unused, required by protocol)house_systems_map (
dict[str,HouseCusps]) – House systems map (unused, required by protocol)house_placements_map (
dict[str,dict[str,int]]) – House placements (unused, required by protocol)
- Return type:
- Returns:
List of FixedStarPosition objects for the requested stars
- property component_name: str
Name of this component.
Antiscia and Contra-Antiscia calculator component.
Antiscia are reflection points across the solstice axis (0° Cancer / 0° Capricorn). Two planets are “in antiscia” when one planet’s antiscion point is conjunct another planet - this is considered a “hidden conjunction.”
Contra-antiscia are reflections across the equinox axis (0° Aries / 0° Libra).
Traditional astrologers use antiscia to find hidden connections between planets that don’t make conventional aspects.
Formulas: - Antiscion = (360° - longitude + 180°) % 360° = (180° - longitude) % 360°
Equivalently: reflect across Cancer-Capricorn axis
Contra-antiscion = (360° - longitude) % 360° Equivalently: reflect across Aries-Libra axis
- class stellium.components.antiscia.AntisciaCalculator(planets=None, orb=1.5, include_contra=True)[source]
Bases:
objectCalculate antiscia and contra-antiscia points and find conjunctions.
Antiscia are reflection points that reveal “hidden” connections between planets. When planet A’s antiscion is conjunct planet B, they are said to be “in antiscia” - a connection as powerful as a conjunction but operating on a hidden or fated level.
- Usage:
- chart = (ChartBuilder.from_native(native)
.add_component(AntisciaCalculator()) .calculate())
# Access antiscia points (added to chart.positions) antiscia_points = [p for p in chart.positions
if p.object_type == ObjectType.ANTISCION]
# Access conjunction data (in metadata) antiscia_data = chart.metadata.get(“antiscia”, {}) conjunctions = antiscia_data.get(“conjunctions”, [])
- calculate(datetime, location, positions, house_systems_map, house_placements_map)[source]
Calculate antiscia and contra-antiscia points.
Returns the antiscia points as CelestialPosition objects. Also populates internal conjunction data accessible via get_metadata().
- Return type:
- property component_name: str
- get_metadata()[source]
Get the calculated antiscia conjunction data.
- Returns:
conjunctions: List of AntisciaConjunction objects
contra_conjunctions: List of contra-antiscia conjunctions
orb: The orb used for calculations
- Return type:
Dictionary containing
- metadata_name = 'antiscia'
- class stellium.components.antiscia.AntisciaConjunction(planet1, planet2, orb, is_applying, antiscion_longitude, planet2_longitude)[source]
Bases:
objectRepresents a conjunction between a planet and another planet’s antiscion.
When planet1’s antiscion point is conjunct planet2, we say “planet1 and planet2 are in antiscia.”
- antiscion_longitude: float
- property description: str
Human-readable description of this antiscia relationship.
- is_applying: bool
- orb: float
- planet1: str
- planet2: str
- planet2_longitude: float
Presentation (stellium.presentation)¶
Report building and rendering.
Report builder for creating chart reports.
The builder pattern allows users to progressively construct reports by adding sections one at a time, then rendering in their chosen format.
- class stellium.presentation.builder.ReportBuilder[source]
Bases:
objectBuilder for chart reports.
Example:
report = ( ReportBuilder() .from_chart(chart) .with_chart_overview() .with_planet_positions() .render(format="rich_table") )
- from_chart(chart)[source]
Set the chart to generate reports from.
- Parameters:
chart (
CalculatedChart|Comparison|MultiChart) – A CalculatedChart, Comparison, or MultiChart- Return type:
- Returns:
Self for chaining
- preset_aspects_only()[source]
Aspects-only preset: Focus on planetary relationships.
Includes: - Chart overview - All aspects (with orbs) - Aspect patterns (Grand Trines, T-Squares, etc.)
- Return type:
- Returns:
Self for chaining
Note: Aspect patterns require AspectPatternAnalyzer component.
Example
>>> report = ReportBuilder().from_chart(chart).preset_aspects_only().render()
- preset_detailed()[source]
Detailed preset: Comprehensive report with all major sections.
Includes: - Chart overview - Moon phase - Planet positions (with speed and all house systems) - Declinations - All aspects (sorted by orb) - House cusps - Essential dignities
- Return type:
- Returns:
Self for chaining
Example
>>> report = ReportBuilder().from_chart(chart).preset_detailed().render()
- preset_full()[source]
Full preset: Everything available.
Includes all sections for maximum detail: - Chart overview - Moon phase - Planet positions (with speed and all house systems) - Declinations - All aspects - Aspect patterns (Grand Trines, T-Squares, etc.) - House cusps - Essential dignities - Midpoints and midpoint aspects - Fixed stars - Zodiacal Releasing (Part of Fortune and Part of Spirit)
Note: Some sections require specific components to be added during chart calculation (e.g., DignityComponent, AspectPatternAnalyzer, MidpointCalculator, FixedStarsComponent, ZodiacalReleasingAnalyzer). Missing components show helpful messages rather than errors.
- Return type:
- Returns:
Self for chaining
Example
>>> chart = (ChartBuilder.from_native(native) ... .with_aspects() ... .add_component(DignityComponent()) ... .add_analyzer(AspectPatternAnalyzer()) ... .add_component(MidpointCalculator()) ... .add_component(FixedStarsComponent()) ... .add_analyzer(ZodiacalReleasingAnalyzer(["Part of Fortune", "Part of Spirit"])) ... .calculate()) >>> report = ReportBuilder().from_chart(chart).preset_full().render()
- preset_minimal()[source]
Minimal preset: Just the basics.
Includes: - Chart overview (name, date, location) - Planet positions
- Return type:
- Returns:
Self for chaining
Example
>>> report = ReportBuilder().from_chart(chart).preset_minimal().render()
- preset_positions_only()[source]
Positions-only preset: Focus on planetary placements.
Includes: - Chart overview - Planet positions (with speed and house placements) - Declinations - House cusps
No aspects or interpretive sections.
- Return type:
- Returns:
Self for chaining
Example
>>> report = ReportBuilder().from_chart(chart).preset_positions_only().render()
- preset_standard()[source]
Standard preset: Common report sections for everyday use.
Includes: - Chart overview - Planet positions (with house placements) - Major aspects (sorted by orb) - House cusps
- Return type:
- Returns:
Self for chaining
Example
>>> report = ReportBuilder().from_chart(chart).preset_standard().render()
- preset_synastry()[source]
Synastry preset: Optimized for relationship comparison charts.
Designed for Comparison objects, this preset shows: - Chart overview (displays both charts’ info) - Planet positions (side-by-side tables for each chart) - Cross-chart aspects (with chart labels) - House cusps (side-by-side tables for each chart)
- Return type:
- Returns:
Self for chaining
Example
>>> comparison = ComparisonBuilder.synastry(chart1, chart2).calculate() >>> report = ReportBuilder().from_chart(comparison).preset_synastry().render()
- preset_transit()[source]
Transit preset: Optimized for transit comparison charts.
Shows natal chart positions alongside transit positions, with cross-chart aspects showing transiting planets’ aspects to natal positions.
Includes: - Chart overview - Planet positions (side-by-side: natal vs transit) - Cross-chart aspects (all aspects, tight orbs) - House cusps (side-by-side)
- Return type:
- Returns:
Self for chaining
Example
>>> transit = ComparisonBuilder.transit(natal, transit_time).calculate() >>> report = ReportBuilder().from_chart(transit).preset_transit().render()
- preset_transit_calendar(end, start=None, include_minor_planets=False)[source]
Transit calendar preset: Sky events over a date range.
Bundles all three transit calendar sections showing what’s happening in the sky between two dates. Useful for planning around retrogrades, sign changes, and eclipses.
Includes: - Planetary stations (retrograde/direct) - Sign ingresses (planets changing signs) - Eclipses (solar and lunar)
- Parameters:
- Return type:
- Returns:
Self for chaining
Example
>>> # Transit calendar for the next year from chart date >>> from datetime import timedelta >>> chart_date = chart.datetime.utc_datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .preset_transit_calendar(end=chart_date + timedelta(days=365)) ... .render()) >>> >>> # Specific date range >>> from datetime import datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .preset_transit_calendar( ... start=datetime(2025, 1, 1), ... end=datetime(2025, 12, 31) ... ) ... .render())
Note
This preset does NOT include natal chart information - it’s purely about sky events. For transits TO your natal chart, use ComparisonBuilder.transit() with preset_transit() instead.
- render(format='rich_table', file=None, show=None)[source]
Render the report with flexible output options.
- Parameters:
- Return type:
- Returns:
Filename if saved to file, None otherwise
- Raises:
ValueError – If no chart has been set
ValueError – If unknown format specified
Examples
# Show in terminal with Rich formatting report.render()
# Save to file (with terminal preview) report.render(format=”plain_table”, file=”chart.txt”)
# Save quietly (no terminal output) report.render(format=”plain_table”, file=”chart.txt”, show=False)
# Generate PDF with chart image and title (configured via builder) report.with_chart_image().with_title(“My Report”).render(
format=”pdf”, file=”report.pdf”
)
- with_arabic_parts(mode='all', show_formula=True, show_description=False)[source]
Add Arabic Parts (Lots) table.
- Parameters:
mode (
str) – Which parts to display (DEFAULT: “all”) - “all”: All calculated parts - “core”: 7 Hermetic Lots (Fortune, Spirit, Eros, etc.) - “family”: Family & Relationship Lots - “life”: Life Topic Lots - “planetary”: Planetary Exaltation Lotsshow_formula (
bool) – Include the formula column (DEFAULT: True) Formula shows as “ASC + Point2 - Point3” with * for sect-aware partsshow_description (
bool) – Include part descriptions (DEFAULT: False)
- Return type:
- Returns:
Self for chaining
Example
>>> # Show all Arabic Parts with formulas >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_arabic_parts() ... .render()) >>> >>> # Show only core Hermetic Lots with descriptions >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_arabic_parts( ... mode="core", ... show_description=True ... ) ... .render())
Note
- Requires ArabicPartsCalculator to be added to chart builder:
from stellium.components.arabic_parts import ArabicPartsCalculator
- chart = (ChartBuilder.from_native(native)
.add_component(ArabicPartsCalculator()) .calculate())
- with_aspect_patterns(pattern_types='all', sort_by='type')[source]
Add aspect patterns table (Grand Trines, T-Squares, Yods, etc.).
- Parameters:
pattern_types (
str|list[str]) – Which pattern types to show (DEFAULT: “all”) - “all”: Show all detected patterns - list[str]: Show specific pattern typessort_by (
str) – How to sort patterns (DEFAULT: “type”) - “type”: Group by pattern type - “element”: Group by element - “count”: Sort by number of planets
- Return type:
- Returns:
Self for chaining
Note
Requires AspectPatternAnalyzer to be added to chart builder. If missing, displays helpful message instead of erroring.
- with_aspects(mode='all', orbs=True, sort_by='orb', include_aspectarian=True, aspectarian_detailed=False, aspectarian_cell_size=None, aspectarian_theme=None)[source]
Add aspects table with optional aspectarian grid.
- Parameters:
mode (
str) – “all”, “major”, “minor”, or “harmonic”orbs (
bool) – Show orb columnsort_by (
str) – How to sort aspects (“orb”, “planet”, or “aspect_type”)include_aspectarian (
bool) – Include aspectarian grid SVG (default: True)aspectarian_detailed (
bool) – Show orb and A/S in aspectarian cells (default: False)aspectarian_cell_size (
int|None) – Override cell size for aspectarian (default: config default)aspectarian_theme (
str|None) – Theme for aspectarian rendering (default: None)
- Return type:
- Returns:
Self for chaining
Note
The aspectarian SVG is displayed in HTML/PDF output. Terminal output shows a placeholder with dimensions.
- with_chart_image(path=None)[source]
Include a chart wheel image in the report.
When called without arguments, automatically generates a chart SVG using the chart’s default draw settings.
- Parameters:
path (
str|None) – Optional path to an existing SVG file. If not provided, a chart image will be auto-generated when rendering.- Return type:
- Returns:
Self for chaining
Examples
# Auto-generate chart image report.with_chart_image()
# Use existing SVG file report.with_chart_image(“my_chart.svg”)
- with_chart_overview()[source]
Add chart overview section (birth data, chart type, etc.).
- Return type:
- Returns:
Self for chaining
- with_cross_aspects(mode='all', orbs=True, sort_by='orb')[source]
Add cross-chart aspects table (for Comparison charts).
Shows aspects between chart1 planets and chart2 planets with appropriate labels for each chart.
- Parameters:
- Return type:
- Returns:
Self for chaining
Note
This section requires a Comparison object (from ComparisonBuilder). If used with a single CalculatedChart, displays a helpful message.
Example
>>> comparison = ComparisonBuilder.synastry(chart1, chart2).calculate() >>> report = (ReportBuilder() ... .from_chart(comparison) ... .with_cross_aspects(mode="major") ... .render())
- with_declination_aspects(mode='all', show_orbs=True, show_oob_status=True, sort_by='orb')[source]
Add declination aspects table (Parallel and Contraparallel).
Declination aspects are based on equatorial coordinates rather than ecliptic longitude. They represent a different type of planetary relationship.
Parallel: Two planets at the same declination (same hemisphere). Interpreted like a conjunction.
Contraparallel: Two planets at equal declination but opposite hemispheres. Interpreted like an opposition.
- Parameters:
mode (
str) – Which aspects to show (DEFAULT: “all”) - “all”: Both parallel and contraparallel - “parallel”: Only parallel aspects - “contraparallel”: Only contraparallel aspectsshow_orbs (
bool) – Show orb column (DEFAULT: True)show_oob_status (
bool) – Show out-of-bounds status (DEFAULT: True)sort_by (
str) – How to sort aspects (DEFAULT: “orb”) - “orb”: Tightest aspects first - “planet”: Group by planet - “aspect_type”: Group by Parallel/Contraparallel
- Return type:
- Returns:
Self for chaining
Note
- Requires .with_declination_aspects() on ChartBuilder:
- chart = (ChartBuilder.from_native(native)
.with_aspects() .with_declination_aspects(orb=1.0) .calculate())
Example
>>> report = (ReportBuilder() ... .from_chart(chart) ... .with_chart_overview() ... .with_declination_aspects(mode="all") ... .render())
- with_declinations()[source]
Add declinations table.
Shows planetary declinations (distance from celestial equator), direction (north/south), and out-of-bounds status.
Out-of-bounds planets have declination beyond the Sun’s maximum (~23°27’) and are considered to have extra intensity or unconventional expression.
- Return type:
- Returns:
Self for chaining
Example
>>> report = (ReportBuilder() ... .from_chart(chart) ... .with_chart_overview() ... .with_declinations() ... .render())
- with_dignities(essential='both', show_details=False)[source]
Add essential dignities table.
- Parameters:
- Return type:
- Returns:
Self for chaining
Note
Requires DignityComponent to be added to chart builder. If missing, displays helpful message instead of erroring.
- with_dispositors(mode='both', rulership='traditional', house_system=None, show_chains=True)[source]
Add dispositor analysis section.
Shows planetary and/or house-based dispositor chains, final dispositor(s), and mutual receptions.
- Parameters:
mode (
str) – Which dispositor analysis to show (DEFAULT: “both”) - “planetary”: Traditional planet-disposes-planet - “house”: Kate’s house-based innovation (life area flow) - “both”: Show both analysesrulership (
str) – “traditional” or “modern” rulership system (DEFAULT: “traditional”)house_system (
str|None) – House system for house-based mode (defaults to chart’s default)show_chains (
bool) – Whether to show full disposition chain details (DEFAULT: True)
- Return type:
- Returns:
Self for chaining
Example
>>> report = (ReportBuilder() ... .from_chart(chart) ... .with_chart_overview() ... .with_dispositors(mode="both") ... .render())
Note
- For graphical output (SVG), use the DispositorEngine directly:
from stellium.engines.dispositors import DispositorEngine, render_both_dispositors engine = DispositorEngine(chart) graph = render_both_dispositors(engine.planetary(), engine.house_based()) graph.render(“dispositors”, format=”svg”)
- with_eclipses(end, start=None, eclipse_types='both')[source]
Add eclipses table.
Shows solar and lunar eclipses within a date range. Useful for eclipse calendars and transit planning.
- Parameters:
- Return type:
- Returns:
Self for chaining
Example
>>> # Eclipses for the next 2 years from chart date >>> from datetime import datetime, timedelta >>> chart_date = chart.datetime.utc_datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_eclipses(end=chart_date + timedelta(days=730)) ... .render()) >>> >>> # Only solar eclipses in a specific range >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_eclipses( ... start=datetime(2025, 1, 1), ... end=datetime(2025, 12, 31), ... eclipse_types="solar" ... ) ... .render())
- with_fixed_stars(tier=None, include_keywords=True, sort_by='longitude')[source]
Add fixed stars table.
Shows positions and metadata for fixed stars in the chart. Requires FixedStarsComponent to be added to chart builder.
- Parameters:
tier (
int|None) – Filter to specific tier (DEFAULT: None = all tiers) - 1: Royal Stars only (Aldebaran, Regulus, Antares, Fomalhaut) - 2: Major Stars only - 3: Extended Stars only - None: All tiersinclude_keywords (
bool) – Include interpretive keywords column (DEFAULT: True)sort_by (
str) – Sort order (DEFAULT: “longitude”) - “longitude”: Zodiacal order - “magnitude”: Brightest first - “tier”: Royal first, then Major, then Extended
- Return type:
- Returns:
Self for chaining
Example
>>> # Royal stars only >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_fixed_stars(tier=1) ... .render()) >>> >>> # All stars sorted by brightness >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_fixed_stars(sort_by="magnitude") ... .render())
Note
- Requires FixedStarsComponent to be added to chart builder:
- chart = (ChartBuilder.from_native(native)
.add_component(FixedStarsComponent()) .calculate())
- with_house_cusps(systems='all')[source]
Add house cusps table.
- with_ingresses(end, start=None, planets=None, include_moon=False, include_minor=False)[source]
Add sign ingresses table.
Shows when planets enter new zodiac signs within a date range. Useful for tracking sign changes and transit planning.
- Parameters:
end (
datetime) – End date for ingress search (required)start (
datetime|None) – Start date for ingress search (optional, defaults to chart date)planets (
list[str] |None) – Which planets to include (default: Sun through Pluto)include_moon (
bool) – Include Moon ingresses (default: False, very frequent)include_minor (
bool) – Include Chiron (default: False)
- Return type:
- Returns:
Self for chaining
Example
>>> # Ingresses for the next year from chart date >>> from datetime import datetime, timedelta >>> chart_date = chart.datetime.utc_datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_ingresses(end=chart_date + timedelta(days=365)) ... .render()) >>> >>> # Specific date range with Moon included >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_ingresses( ... start=datetime(2025, 1, 1), ... end=datetime(2025, 12, 31), ... include_moon=True ... ) ... .render())
- with_midpoint_aspects(mode='conjunction', orb=1.5, midpoint_filter='all', sort_by='orb')[source]
Add planets aspecting midpoints table.
This shows which planets activate which midpoints - the most useful way to interpret midpoints. Typically only conjunctions matter (1-2° orb).
- Parameters:
mode (
str) – Which aspects to check (DEFAULT: “conjunction”) - “conjunction”: Only conjunctions (most common, recommended) - “hard”: Conjunction, square, opposition - “all”: All major aspectsorb (
float) – Maximum orb in degrees (DEFAULT: 1.5°) Midpoints use tighter orbs than regular aspects.midpoint_filter (
str) – Which midpoints to check (DEFAULT: “all”) - “all”: All calculated midpoints - “core”: Only Sun/Moon/ASC/MC midpointssort_by (
str) – Sort order (DEFAULT: “orb”) - “orb”: Tightest aspects first - “planet”: Group by aspecting planet - “midpoint”: Group by midpoint
- Return type:
- Returns:
Self for chaining
Example
>>> # Show planets conjunct any midpoint within 1.5° >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_midpoint_aspects() ... .render()) >>> >>> # Show hard aspects to core midpoints only >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_midpoint_aspects( ... mode="hard", ... midpoint_filter="core", ... orb=2.0 ... ) ... .render())
Note
- Requires MidpointCalculator to be added to chart builder:
- chart = (ChartBuilder.from_native(native)
.add_component(MidpointCalculator()) .calculate())
- with_midpoint_trees(tree_bases=None, branch_objects=None, orb=1.5, aspect_mode='conjunction', output='both')[source]
Add midpoint tree visualization section.
Generates tree diagrams showing which midpoints aspect focal points. This is a standard Uranian/Hamburg astrology technique for interpreting planetary pictures.
- Parameters:
tree_bases (
list[str] |None) – Focal points to build trees for (DEFAULT: Sun, Moon, MC, ASC)branch_objects (
list[str] |None) – Objects to include in midpoint pairs. Default: 10 planets + ASC + MC + True Nodeorb (
float) – Maximum orb in degrees (DEFAULT: 1.5°)aspect_mode (
str) – Which aspects to check (DEFAULT: “conjunction”) - “conjunction”: Only conjunctions (0°) - “hard”: Conjunction + 45° series (0°, 45°, 90°, 135°, 180°) - “all”: All major aspectsoutput (
str) – What to generate (DEFAULT: “both”) - “svg”: Just SVG visualization - “text”: Just text output - “both”: Both SVG and text
- Return type:
- Returns:
Self for chaining
Example
>>> # Show midpoint trees with hard aspects >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_midpoint_trees(aspect_mode="hard") ... .render()) >>> >>> # Custom focal points with conjunction only >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_midpoint_trees( ... tree_bases=["Sun", "Moon"], ... orb=2.0, ... aspect_mode="conjunction" ... ) ... .render())
Note
- Requires MidpointCalculator to be added to chart builder:
- chart = (ChartBuilder.from_native(native)
.add_component(MidpointCalculator()) .calculate())
- with_midpoints(mode='all', threshold=None)[source]
Add midpoints table.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_moon_phase()[source]
Add moon phase section.
- Return type:
- with_planet_positions(include_speed=False, include_house=True, house_systems='all')[source]
Add planet positions table.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_profections(age=None, date=None, include_monthly=True, include_multi_point=True, include_timeline=False, timeline_range=None, points=None, house_system=None, rulership='traditional')[source]
Add profection timing analysis section.
Profections are a Hellenistic technique where the ASC advances one sign per year. The planet ruling that sign becomes the “Lord of the Year.”
- Parameters:
age (
int|None) – Age for profection (either age OR date required)date (
str|None) – Target date as ISO string (e.g., “2025-06-15”)include_monthly (
bool) – Show monthly profection when date is providedinclude_multi_point (
bool) – Show lords for ASC, Sun, Moon, MCinclude_timeline (
bool) – Show timeline table of Lordstimeline_range (
tuple[int,int] |None) – Custom range for timeline (e.g., (25, 40))points (
list[str] |None) – Custom points for multi-point analysishouse_system (
str|None) – House system to use (default: prefers Whole Sign)rulership (
str) – “traditional” or “modern”
- Return type:
- Returns:
Self for chaining
Example:
# By age report = ( ReportBuilder() .from_chart(chart) .with_profections(age=30) .render() ) # By date with timeline report = ( ReportBuilder() .from_chart(chart) .with_profections(date="2025-06-15", include_timeline=True) .render() )
- with_profections_wheel(age=None, date=None, compare_ages=None, show_wheel=True, show_table=True, house_system=None, rulership='traditional')[source]
Add profection wheel visualization section.
Generates a visual wheel diagram showing annual profections: - Circular wheel with ages 0-95 spiraling through 12 houses - Zodiac signs and house labels around the perimeter - Natal planet positions marked on the wheel - Current age highlighted - Summary table with profection details
- Parameters:
age (
int|None) – Current age to highlight (either age OR date required)date (
str|None) – Target date as ISO string (e.g., “2025-06-15”)compare_ages (
list[int] |None) – List of ages to compare in table (default: current and next)show_wheel (
bool) – Whether to show the wheel visualization (default: True)show_table (
bool) – Whether to show the summary table (default: True)house_system (
str|None) – House system to use (default: prefers Whole Sign)rulership (
str) – “traditional” or “modern”
- Return type:
- Returns:
Self for chaining
Example:
# By age with both wheel and table report = ( ReportBuilder() .from_chart(chart) .with_profections_wheel(age=30) .render(format="pdf", file="profections.pdf") ) # Compare specific ages report = ( ReportBuilder() .from_chart(chart) .with_profections_wheel( age=30, compare_ages=[30, 31, 32] ) .render() )
- with_section(section)[source]
Add a custom section.
This allows users to extend the report system with their own sections.
- Parameters:
section (
ReportSection) – Any object implementing the ReportSection protocol- Return type:
- Returns:
Self for chaining
Example:
class MyCustomSection: @property def section_name(self) -> str: return "My Analysis" def generate_data(self, chart: CalculatedChart) -> dict: return {"type": "text", "text": "Custom analysis..."} report = ( ReportBuilder() .from_chart(chart) .with_section(MyCustomSection()) .render() )
- with_stations(end, start=None, planets=None, include_minor=False)[source]
Add planetary stations (retrograde/direct) table.
Shows when planets station retrograde or direct within a date range. Useful for retrograde calendars and transit planning.
- Parameters:
- Return type:
- Returns:
Self for chaining
Example
>>> # Stations for the next year from chart date >>> from datetime import datetime, timedelta >>> chart_date = chart.datetime.utc_datetime >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_stations(end=chart_date + timedelta(days=365)) ... .render()) >>> >>> # Specific date range >>> report = (ReportBuilder() ... .from_chart(chart) ... .with_stations( ... start=datetime(2025, 1, 1), ... end=datetime(2025, 12, 31) ... ) ... .render())
- with_title(title)[source]
Set a custom title for the report.
The title appears on the cover page of PDF reports. If not set, a default title is generated from the chart’s name.
- Parameters:
title (
str) – Custom title string- Return type:
- Returns:
Self for chaining
Examples
report.with_title(“Birth Chart Analysis”) report.with_title(“Albert Einstein - Complete Natal Analysis”)
- with_zodiacal_releasing(lots=None, mode='both', query_date=None, query_age=None, context_periods=2)[source]
Add Zodiacal Releasing timing analysis section.
Zodiacal Releasing is a Hellenistic predictive technique that divides life into major periods ruled by signs, showing when different life themes are activated.
- Parameters:
lots (
str|list[str] |None) – Which lot(s) to display: - str: Single lot name (e.g., “Part of Fortune”) - list[str]: Multiple lots (e.g., [“Part of Fortune”, “Part of Spirit”]) - None: All lots calculated in the chart (DEFAULT)mode (
str) – Display mode: - “snapshot”: Current periods only - “timeline”: L1 timeline only - “both”: Both snapshot and timeline (DEFAULT)query_date (
str|None) – Date for snapshot as ISO string (defaults to now)query_age (
float|None) – Age for snapshot (alternative to query_date)context_periods (
int) – Number of L3/L4 periods to show before/after current (default: 2)
- Return type:
- Returns:
Self for chaining
Note
Requires ZodiacalReleasingAnalyzer to be added during chart calculation:
from stellium.engines.releasing import ZodiacalReleasingAnalyzer
- chart = (
ChartBuilder.from_native(native) .add_analyzer(ZodiacalReleasingAnalyzer([“Part of Fortune”, “Part of Spirit”])) .calculate()
)
Example:
# Show current ZR state for all calculated lots report = ( ReportBuilder() .from_chart(chart) .with_zodiacal_releasing() .render() ) # Show ZR for specific lot at specific age report = ( ReportBuilder() .from_chart(chart) .with_zodiacal_releasing( lots="Part of Fortune", mode="snapshot", query_age=30 ) .render() ) # Show only L1 timeline for Fortune and Spirit report = ( ReportBuilder() .from_chart(chart) .with_zodiacal_releasing( lots=["Part of Fortune", "Part of Spirit"], mode="timeline" ) .render() )
- with_zr_visualization(lot='Part of Fortune', year=None, levels=(1, 2, 3), output='both')[source]
Add Zodiacal Releasing visualization (SVG timeline diagram).
Generates visual timeline diagrams in Honeycomb Collective style: - Overview page: natal angles chart + period length reference - Timeline page: stacked L1/L2/L3 timelines with peak shapes
- Parameters:
lot (
str) – Which lot to visualize (default: “Part of Fortune”)year (
int|None) – Year to visualize (defaults to current year)levels (
tuple[int,...]) – Which levels to show in timeline (default: 1, 2, 3)output (
str) – What to generate: - “overview”: Just the overview page - “timeline”: Just the timeline visualization - “both”: Both pages (DEFAULT)
- Return type:
- Returns:
Self for chaining
Note
Requires ZodiacalReleasingAnalyzer to be added during chart calculation:
from stellium.engines.releasing import ZodiacalReleasingAnalyzer
- chart = (
ChartBuilder.from_native(native) .add_analyzer(ZodiacalReleasingAnalyzer([“Part of Fortune”])) .calculate()
)
Example:
# Add ZR visualization to PDF report report = ( ReportBuilder() .from_chart(chart) .with_chart_overview() .with_zr_visualization(lot="Part of Fortune", year=2025) .render(format="pdf", file="report.pdf") )
Output renderers for reports.
Renderers take structured data from sections and format it for different output mediums (terminal with Rich, plain text, PDF, HTML, etc.).
- class stellium.presentation.renderers.HTMLRenderer(css_style=None)[source]
Bases:
objectRenderer that converts report sections to HTML.
Can be used directly for HTML output or as input to PDFRenderer. Generates clean, semantic HTML with embedded CSS styling.
- render_report(sections, chart_svg_content=None)[source]
Render complete report to HTML string.
- class stellium.presentation.renderers.PlainTextRenderer[source]
Bases:
objectPlain text renderer with no dependencies.
Creates simple ASCII tables and formatted text suitable for: - Log files - Email - Systems without Rich library - Piping to other tools
- class stellium.presentation.renderers.ProseRenderer(bullet='•')[source]
Bases:
objectRenderer that converts structured section data to natural language prose.
Designed for pasting chart info into conversations with AI friends or anywhere you want clean, readable text without tables or formatting codes.
Output format: - Chart overview as flowing sentences - Lists of positions/aspects as bullet points - No tables, no headers, no special formatting
- Example output:
Kate Louie was born on January 6, 1994 at 11:47 AM in Mountain View, CA. This is a day chart with Aries rising. The chart ruler is Mars.
Planet Positions: • The Sun is at 15°52’ Capricorn in the 9th house • The Moon is at 22°14’ Scorpio in the 6th house …
- class stellium.presentation.renderers.RichTableRenderer[source]
Bases:
objectRenderer using the Rich library for beautiful terminal output.
Requires: pip install rich
Features: - Colored tables with borders - Automatic column width adjustment - Unicode box characters
- print_report(sections)[source]
Print report directly to terminal with Rich formatting.
This method prints the report with full ANSI colors and styling, intended for immediate terminal display.
- Return type:
- render_report(sections)[source]
Render complete report to plaintext string (ANSI codes stripped).
Used for file output and testing. Returns clean text without ANSI escape codes.
- Return type:
- class stellium.presentation.renderers.TypstRenderer[source]
Bases:
objectRenderer that creates beautiful PDFs using Typst typesetting.
Typst is a modern typesetting system with LaTeX-quality output but much simpler syntax and faster compilation.
Requires: pip install typst
Features: - Professional typography (kerning, ligatures, hyphenation) - Clean table styling with alternating row colors - Proper font handling for astrological symbols - Embedded SVG chart support - Page headers/footers with page numbers
- render_report(sections, output_file=None, chart_svg_path=None, title='Astrological Report')[source]
Render complete report to PDF using Typst.
Report section implementations.
Each section extracts specific data from a CalculatedChart and formats it into a standardized structure that renderers can consume.
This package contains domain-organized section modules: - core: Basic chart info (ChartOverview, PlanetPosition, HouseCusps) - aspects: Aspect-related (AspectSection, AspectPatternSection, CrossChartAspectSection) - dignities: Dignity analysis (DignitySection, DispositorSection) - midpoints: Midpoint analysis (MidpointSection, MidpointAspectsSection) - timing: Time lord techniques (ZodiacalReleasingSection, ProfectionSection) - misc: Other sections (MoonPhase, Declination, FixedStars, ArabicParts, etc.)
- class stellium.presentation.sections.AntisciaSection(include_contra=True, show_points=False)[source]
Bases:
objectTable of Antiscia and Contra-Antiscia conjunctions.
Antiscia are “hidden conjunctions” - when one planet’s reflection point (across the solstice axis) is conjunct another planet. Contra-antiscia are reflections across the equinox axis.
Shows: - The two planets involved - Whether it’s antiscia or contra-antiscia - The orb of the conjunction - Whether the aspect is applying or separating
- property section_name: str
- class stellium.presentation.sections.ArabicPartsSection(mode='all', show_formula=True, show_description=False)[source]
Bases:
objectTable of Arabic Parts (Lots).
Shows calculated Arabic Parts with their positions, house placements, and optionally their formulas and descriptions.
Modes: - “all”: All calculated parts - “core”: 7 Hermetic Lots (Fortune, Spirit, Eros, Necessity, Courage, Victory, Nemesis) - “family”: Family & Relationship Lots (Father, Mother, Marriage, Children, Siblings) - “life”: Life Topic Lots (Action, Profession, Passion, Illness, Death, etc.) - “planetary”: Planetary Exaltation Lots (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn)
- CORE_PARTS = {'Part of Courage (Tolma)', 'Part of Eros (Love)', 'Part of Eros (Planetary)', 'Part of Fortune', 'Part of Necessity (Ananke)', 'Part of Nemesis', 'Part of Spirit', 'Part of Victory (Nike)'}
- FAMILY_PARTS = {'Part of Children', 'Part of Father', 'Part of Marriage', 'Part of Mother', 'Part of Siblings'}
- LIFE_PARTS = {'Part of Action (Praxis)', 'Part of Death', 'Part of Debt / Bondage', 'Part of Friends / Associates', 'Part of Illness / Disease', 'Part of Passion / Lust', 'Part of Profession (User)', 'Part of Travel'}
- PLANETARY_PARTS = {'Part of Jupiter (Exaltation)', 'Part of Mars (Exaltation)', 'Part of Mercury (Exaltation)', 'Part of Saturn (Exaltation)', 'Part of Venus (Exaltation)', 'Part of the Moon (Exaltation)', 'Part of the Sun (Exaltation)'}
- property section_name: str
- class stellium.presentation.sections.AspectPatternSection(pattern_types='all', sort_by='type')[source]
Bases:
objectTable of detected aspect patterns.
Shows Grand Trines, T-Squares, Yods, etc. Gracefully handles missing pattern data with helpful message.
- generate_data(chart)[source]
Generate aspect pattern table.
For MultiChart/Comparison, shows patterns for each chart grouped by label.
- property section_name: str
- class stellium.presentation.sections.AspectSection(mode='all', orbs=True, sort_by='orb', include_aspectarian=True, aspectarian_detailed=False, aspectarian_cell_size=None, aspectarian_theme=None)[source]
Bases:
objectTable of aspects between planets.
Shows: - Planet 1 - Aspect type - Planet 2 - Orb (optional) - Applying/Separating (optional)
Optionally includes an aspectarian grid SVG (triangle for single charts).
- generate_data(chart)[source]
Generate aspects table with optional aspectarian SVG.
For MultiChart/Comparison, shows each chart’s internal aspects grouped by chart label.
- property section_name: str
- class stellium.presentation.sections.CacheInfoSection[source]
Bases:
objectDisplay cache statistics in reports.
- property section_name: str
- class stellium.presentation.sections.ChartOverviewSection[source]
Bases:
objectOverview section with basic chart information.
Shows: - Native name (if available) - Birth date/time - Location - Chart type (day/night) - House system
For Comparison objects, shows info for both charts.
- generate_data(chart)[source]
Generate chart overview data.
For Comparison/MultiChart objects, shows all charts’ information.
Why key-value format? - Simple label: value pairs - Easy to render as a list or small table - Human-readable structure
- property section_name: str
- class stellium.presentation.sections.CrossChartAspectSection(mode='all', orbs=True, sort_by='orb')[source]
Bases:
objectTable of cross-chart aspects for Comparison charts.
Shows aspects between chart1 planets and chart2 planets: - Chart 1 Planet (with label) - Aspect type - Chart 2 Planet (with label) - Orb (optional) - Applying/Separating (optional)
- property section_name: str
- class stellium.presentation.sections.DeclinationAspectSection(mode='all', show_orbs=True, show_oob_status=True, sort_by='orb')[source]
Bases:
objectTable of declination aspects (Parallel and Contraparallel).
Shows: - Planet 1 (with glyph) - Aspect type (Parallel ∥ or Contraparallel ⋕) - Planet 2 (with glyph) - Orb (optional) - Out-of-bounds status (if either planet is OOB)
- property section_name: str
- class stellium.presentation.sections.DeclinationSection[source]
Bases:
objectTable of planetary declinations.
Shows: - Planet name with glyph - Declination value (degrees north/south of celestial equator) - Direction (North/South) - Out-of-bounds status
- generate_data(chart)[source]
Generate declination table data.
For MultiChart/Comparison, shows declinations for each chart grouped by label. Shows declination values for all planets with equatorial coordinates. Highlights out-of-bounds planets (beyond Sun’s max declination).
- property section_name: str
- class stellium.presentation.sections.DignitySection(essential='both', show_details=False)[source]
Bases:
objectTable of essential dignities for planets.
Shows dignity scores and details for traditional and/or modern systems. Gracefully handles missing dignity data with helpful message.
- generate_data(chart)[source]
Generate dignity table.
For MultiChart/Comparison, shows dignities for each chart grouped by label.
- property section_name: str
- class stellium.presentation.sections.DispositorSection(mode='both', rulership='traditional', house_system=None, show_chains=True)[source]
Bases:
objectDispositor analysis section.
Shows planetary and/or house-based dispositor chains, final dispositor(s), and mutual receptions. Text summary only - graphviz rendering is separate.
Example
>>> section = DispositorSection(mode="both") >>> data = section.generate_data(chart)
- generate_data(chart)[source]
Generate dispositor analysis.
For MultiChart/Comparison, shows dispositors for each chart grouped by label. Returns a compound section with subsections for planetary and/or house dispositors, each showing final dispositor and mutual receptions.
- property section_name: str
- class stellium.presentation.sections.EclipseSection(start, end, eclipse_types='both')[source]
Bases:
objectEclipse report section.
Shows solar and lunar eclipses within a date range. Useful for eclipse calendars and transit planning.
Note: This section uses explicit start/end dates rather than analyzing the natal chart. The chart parameter in generate_data() is accepted for protocol compliance but not used internally.
- generate_data(chart)[source]
Generate eclipse data for the date range.
- Parameters:
chart (
CalculatedChart) – CalculatedChart (accepted for protocol, not used internally)- Return type:
- Returns:
Dictionary with eclipse data for rendering
- property section_name: str
- class stellium.presentation.sections.FixedStarsSection(tier=None, include_keywords=True, sort_by='longitude')[source]
Bases:
objectTable of fixed star positions.
Shows: - Star name with glyph - Zodiac position (sign + degree) - Constellation - Magnitude (brightness) - Traditional planetary nature - Keywords
- property section_name: str
- class stellium.presentation.sections.HouseCuspsSection(systems='all')[source]
Bases:
objectTable of house cusp positions for multiple house systems.
Shows: - House number (1-12) - Cusp position for each calculated house system
- generate_data(chart)[source]
Generate house cusps table.
For Comparison/MultiChart objects, generates side-by-side tables for each chart.
- property section_name: str
- class stellium.presentation.sections.IngressSection(start, end, planets=None, include_moon=False, include_minor=False)[source]
Bases:
objectSign ingress report section.
Shows when planets enter new zodiac signs within a date range. Useful for tracking sign changes and transit planning.
Note: This section uses explicit start/end dates rather than analyzing the natal chart. The chart parameter in generate_data() is accepted for protocol compliance but not used internally.
- generate_data(chart)[source]
Generate ingress data for the date range.
- Parameters:
chart (
CalculatedChart) – CalculatedChart (accepted for protocol, not used internally)- Return type:
- Returns:
Dictionary with ingress data for rendering
- property section_name: str
- class stellium.presentation.sections.MidpointAspectsSection(mode='conjunction', orb=1.5, midpoint_filter='all', sort_by='orb')[source]
Bases:
objectTable of planets aspecting midpoints.
This is what most people care about with midpoints: which planets activate which midpoints? Typically conjunctions are most important (1-2° orb), but hard aspects (square, opposition) can also be shown.
Shows: - Planet that aspects the midpoint - Aspect type (conjunction, square, etc.) - Midpoint being aspected (e.g., “Sun/Moon”) - Orb in degrees
- ASPECT_ANGLES = {'Conjunction': 0, 'Opposition': 180, 'Sextile': 60, 'Square': 90, 'Trine': 120}
- CORE_OBJECTS = {'ASC', 'MC', 'Moon', 'Sun'}
- generate_data(chart)[source]
Generate midpoint aspects table.
For MultiChart/Comparison, shows midpoint aspects for each chart grouped by label.
- property section_name: str
- class stellium.presentation.sections.MidpointSection(mode='all', threshold=None)[source]
Bases:
objectTable of midpoints.
Shows: - Midpoint pair (e.g., “Sun/Moon”) - Degree position - Sign
- CORE_OBJECTS = {'ASC', 'MC', 'Moon', 'Sun'}
- generate_data(chart)[source]
Generate midpoints table.
For MultiChart/Comparison, shows midpoints for each chart grouped by label.
- property section_name: str
- class stellium.presentation.sections.MidpointTreeSection(tree_bases=None, branch_objects=None, orb=1.5, aspect_mode='conjunction', output='both')[source]
Bases:
objectMidpoint Tree visualization section.
Generates tree diagrams showing which midpoints aspect focal points. Standard technique in Uranian/Hamburg astrology for interpreting planetary pictures.
For each focal point (default: Sun, Moon, MC, ASC), shows all midpoints that aspect it within the configured orb.
Example:
section = MidpointTreeSection( tree_bases=["Sun", "Moon", "MC", "ASC"], orb=1.5, aspect_mode="hard", # conjunction + 45° series output="both" )
- ALL_ASPECTS = {'Conjunction': 0, 'Opposition': 180, 'Sextile': 60, 'Square': 90, 'Trine': 120}
- DEFAULT_BRANCH_OBJECTS = ['Sun', 'Moon', 'Mercury', 'Venus', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto', 'ASC', 'MC', 'True Node']
- DEFAULT_TREE_BASES = ['Sun', 'Moon', 'MC', 'ASC']
- HARD_ASPECTS = {'Conjunction': 0, 'Opposition': 180, 'Semisquare': 45, 'Sesquisquare': 135, 'Square': 90}
- property section_name: str
- class stellium.presentation.sections.MoonPhaseSection[source]
Bases:
objectDisplay Moon phase information.
- property section_name: str
- class stellium.presentation.sections.PlanetPositionSection(include_speed=False, include_house=True, house_systems='all')[source]
Bases:
objectTable of planet positions.
Shows: - Planet name - Sign + degree - House (optional) - Speed (optional, shows retrograde status)
- generate_data(chart)[source]
Generate planet positions table.
For Comparison/MultiChart objects, generates side-by-side tables for each chart.
- property section_name: str
- class stellium.presentation.sections.ProfectionSection(age=None, date=None, include_monthly=True, include_multi_point=True, include_timeline=False, timeline_range=None, points=None, house_system=None, rulership='traditional')[source]
Bases:
objectProfection timing analysis section.
Shows annual profections with Lord of the Year, activated house, and optionally monthly profections and multi-point analysis.
- property section_name: str
- class stellium.presentation.sections.ProfectionVisualizationSection(age=None, date=None, compare_ages=None, show_wheel=True, show_table=True, house_system=None, rulership='traditional')[source]
Bases:
objectProfection wheel visualization section.
Generates an SVG visualization showing: - Circular wheel with ages spiraling through 12 houses - Zodiac signs and house labels around the perimeter - Natal planet positions - Current age highlighted - Summary table with profection details
Example
- section = ProfectionVisualizationSection(
age=30, show_table=True
) data = section.generate_data(chart) # data[“content”] contains SVG string
- property section_name: str
- class stellium.presentation.sections.StationSection(start, end, planets=None, include_minor=False)[source]
Bases:
objectPlanetary stations report section.
Shows when planets station retrograde or direct within a date range. Useful for retrograde calendars and transit planning.
Note: This section uses explicit start/end dates rather than analyzing the natal chart. The chart parameter in generate_data() is accepted for protocol compliance but not used internally.
- generate_data(chart)[source]
Generate station data for the date range.
- Parameters:
chart (
CalculatedChart) – CalculatedChart (accepted for protocol, not used internally)- Return type:
- Returns:
Dictionary with station data for rendering
- property section_name: str
- class stellium.presentation.sections.TransitGanttSection(start, end, transit_planets=None, aspects=None, include_natal_points=None, width=900, row_height=14, exclude_fast_planets=True, theme='dark')[source]
Bases:
objectNatal transit periods as an SVG Gantt timeline chart.
Each row is a transit event; bars span the orb window; tick marks indicate exact dates. Rows are grouped by transiting planet.
Defaults to excluding fast planets (Sun, Moon, Mercury, Venus, Mars) since their many short transits make the chart unreadable. Use TransitListSection for comprehensive fast-planet output.
Usage:
section = TransitGanttSection( start=datetime(2025, 12, 1), end=datetime(2026, 6, 1), ) report = ReportBuilder().from_chart(natal_chart).add_section(section) report.render(format="pdf")
- generate_data(chart)[source]
Calculate and return transit periods as an SVG Gantt chart.
- Parameters:
chart (
CalculatedChart) – The natal CalculatedChart to transit to.- Return type:
- Returns:
Dict with type=”svg” and SVG content string.
- property section_name: str
- class stellium.presentation.sections.TransitListSection(start, end, transit_planets=None, aspects=None, include_natal_points=None, exclude_fast_planets=False)[source]
Bases:
objectNatal transit aspect periods as a plain-text list.
Shows when transiting planets form aspects to natal positions, with orb entry/exit dates, e.g.:
Dec 2 - Mar 2 '26 — Jupiter △ natal Chiron Dec 4 — Mercury □ natal Jupiter Jan 8 - Feb 6 '26 — Uranus △ natal Neptune (3x: Jan 15, Jan 30, Feb 12)
Usage:
section = TransitListSection( start=datetime(2025, 12, 1), end=datetime(2026, 6, 1), ) report = ReportBuilder().from_chart(natal_chart).add_section(section) report.render()
Note: The chart parameter in generate_data() is the natal chart.
- generate_data(chart)[source]
Calculate and return transit periods as text rows.
- Parameters:
chart (
CalculatedChart) – The natal CalculatedChart to transit to.- Return type:
- Returns:
Dict with type=”text” and structured period data.
- property section_name: str
- class stellium.presentation.sections.TransitPeriod(transit_planet, natal_planet, aspect_name, aspect_angle, orb, exact_dates, start, end)[source]
Bases:
objectA single transit event: one transiting planet forming one aspect to one natal point.
Includes the orb entry/exit window and all exact dates within the window. Multi-pass transits (retrograde causing 2–3 exact crossings) are represented as a single TransitPeriod with multiple exact_dates entries.
- transit_planet
Name of the transiting planet (e.g. “Jupiter”)
- natal_planet
Name of the natal point being aspected (e.g. “Sun”)
- aspect_name
Name of the aspect (e.g. “Trine”)
- aspect_angle
Aspect angle in degrees (e.g. 120.0)
- orb
Orb used for this calculation in degrees
- exact_dates
One or more exact-aspect datetimes (UTC); 2–3 = retrograde passes
- start
When transit entered orb, or None if before the search window
- end
When transit exits orb, or None if extends beyond search window
- aspect_angle: float
- aspect_name: str
- property duration_days: float | None
Duration in days, or None if window extends outside the search range.
- property is_multi_pass: bool
True if the transit crosses the exact point more than once (retrograde).
- natal_planet: str
- orb: float
- property peak_date: datetime
The middle exact date — most representative for sorting.
- transit_planet: str
- class stellium.presentation.sections.ZRVisualizationSection(lot='Part of Fortune', year=None, start_date=None, end_date=None, levels=(1, 2, 3), highlight_date=None, output='both')[source]
Bases:
objectZodiacal Releasing visualization section.
Generates SVG timeline visualizations in Honeycomb Collective style: - Overview page: natal angles chart + period length reference - Timeline page: stacked L1/L2/L3 timelines with peak shapes
Returns SVG content that can be embedded in PDF planners or reports.
Example
- section = ZRVisualizationSection(
lot=”Part of Fortune”, year=2025, output=”timeline” # or “overview” or “both”
) data = section.generate_data(chart) # data[“content”] contains SVG string
- generate_data(chart)[source]
Generate ZR visualization data.
- property section_name: str
- class stellium.presentation.sections.ZodiacalReleasingSection(lots=None, mode='both', query_date=None, query_age=None, context_periods=2)[source]
Bases:
objectZodiacal Releasing timing analysis section.
Shows ZR periods from one or more Lots (Fortune, Spirit, etc.), with options to display current snapshot and/or L1 timeline.
Snapshot mode shows: - Current L1/L2 periods (always shown) - L3/L4 context (current ± 2 periods) for finer timing
Timeline mode shows: - All L1 periods with ages and status indicators - Peak (★), Angular (◆), and Current (⚡) markers
- property section_name: str
- stellium.presentation.sections.abbreviate_house_system(system_name)[source]
Generate 2-4 character abbreviation for house system names.
- Parameters:
system_name (
str) – Full house system name (e.g., “Placidus”, “Whole Sign”)- Return type:
- Returns:
Short abbreviation (e.g., “Pl”, “WS”)
Example
>>> abbreviate_house_system("Placidus") 'Pl' >>> abbreviate_house_system("Whole Sign") 'WS'
- stellium.presentation.sections.calculate_transit_periods(natal_chart, start, end, transit_planets=None, aspects=None, include_natal_points=None)[source]
Calculate transit-to-natal aspect periods for a date range.
For each (transiting planet × natal point × aspect) combination, returns TransitPeriod objects with orb entry/exit dates and all exact dates within the window, including multiple passes from retrograde motion.
Reuses stellium.engines.search functions for all ephemeris lookups — no raw Swiss Ephemeris calls here.
- Parameters:
natal_chart (
CalculatedChart) – The natal CalculatedChart to transit to.start (
datetime) – Start of date range (UTC).end (
datetime) – End of date range (UTC).transit_planets (
list[str] |None) – Planets to use as transits (default: all 12).aspects (
dict[str,float] |None) – Dict of {aspect_name: orb_degrees} (default: 5 major).include_natal_points (
list[str] |None) – Limit natal points checked (default: all planets).
- Return type:
list[TransitPeriod]- Returns:
List of TransitPeriod objects sorted by start date (or first exact date if start is outside the search window).
- stellium.presentation.sections.get_aspect_display(aspect_name)[source]
Get display name and glyph for an aspect.
- stellium.presentation.sections.get_aspect_sort_key(aspect_name)[source]
Generate sort key for consistent aspect ordering in reports.
Sorting hierarchy: 1. Registry insertion order (aspects ordered by angle: 0°, 60°, 90°, etc.) 2. Angle value (for aspects not in registry) 3. Alphabetical name (final fallback)
- Parameters:
aspect_name (
str) – Name of the aspect (e.g., “Conjunction”, “Trine”)- Return type:
- Returns:
Tuple sort key for use with sorted()
Example
aspects = sorted(aspects, key=lambda a: get_aspect_sort_key(a.aspect_name))
- stellium.presentation.sections.get_object_display(name)[source]
Get display name and glyph for a celestial object.
- stellium.presentation.sections.get_object_sort_key(position)[source]
Generate sort key for consistent object ordering in reports.
Sorting hierarchy: 1. Object type (Planet < Node < Point < Asteroid < Angle < Midpoint) 2. Registry insertion order (for registered objects) 3. Swiss Ephemeris ID (for unregistered known objects) 4. Alphabetical name (for custom objects)
- Parameters:
position – A celestial object position from CalculatedChart
- Returns:
Tuple sort key for use with sorted()
Example
positions = sorted(chart.positions, key=get_object_sort_key)
- stellium.presentation.sections.get_sign_glyph(sign_name)[source]
Get the zodiac glyph for a sign name.
- Return type:
Core report sections for basic chart information.
Includes: - ChartOverviewSection: Basic chart metadata (date, time, location) - PlanetPositionSection: Positions of celestial objects - HouseCuspsSection: House cusp positions for multiple systems
- class stellium.presentation.sections.core.ChartOverviewSection[source]
Bases:
objectOverview section with basic chart information.
Shows: - Native name (if available) - Birth date/time - Location - Chart type (day/night) - House system
For Comparison objects, shows info for both charts.
- generate_data(chart)[source]
Generate chart overview data.
For Comparison/MultiChart objects, shows all charts’ information.
Why key-value format? - Simple label: value pairs - Easy to render as a list or small table - Human-readable structure
- property section_name: str
- class stellium.presentation.sections.core.HouseCuspsSection(systems='all')[source]
Bases:
objectTable of house cusp positions for multiple house systems.
Shows: - House number (1-12) - Cusp position for each calculated house system
- generate_data(chart)[source]
Generate house cusps table.
For Comparison/MultiChart objects, generates side-by-side tables for each chart.
- property section_name: str
- class stellium.presentation.sections.core.PlanetPositionSection(include_speed=False, include_house=True, house_systems='all')[source]
Bases:
objectTable of planet positions.
Shows: - Planet name - Sign + degree - House (optional) - Speed (optional, shows retrograde status)
- generate_data(chart)[source]
Generate planet positions table.
For Comparison/MultiChart objects, generates side-by-side tables for each chart.
- property section_name: str
Aspect-related report sections.
Includes: - AspectSection: Table of aspects between planets - AspectPatternSection: Detected aspect patterns (Grand Trines, T-Squares, etc.) - CrossChartAspectSection: Cross-chart aspects for synastry/comparison
- class stellium.presentation.sections.aspects.AspectPatternSection(pattern_types='all', sort_by='type')[source]
Bases:
objectTable of detected aspect patterns.
Shows Grand Trines, T-Squares, Yods, etc. Gracefully handles missing pattern data with helpful message.
- generate_data(chart)[source]
Generate aspect pattern table.
For MultiChart/Comparison, shows patterns for each chart grouped by label.
- property section_name: str
- class stellium.presentation.sections.aspects.AspectSection(mode='all', orbs=True, sort_by='orb', include_aspectarian=True, aspectarian_detailed=False, aspectarian_cell_size=None, aspectarian_theme=None)[source]
Bases:
objectTable of aspects between planets.
Shows: - Planet 1 - Aspect type - Planet 2 - Orb (optional) - Applying/Separating (optional)
Optionally includes an aspectarian grid SVG (triangle for single charts).
- generate_data(chart)[source]
Generate aspects table with optional aspectarian SVG.
For MultiChart/Comparison, shows each chart’s internal aspects grouped by chart label.
- property section_name: str
- class stellium.presentation.sections.aspects.CrossChartAspectSection(mode='all', orbs=True, sort_by='orb')[source]
Bases:
objectTable of cross-chart aspects for Comparison charts.
Shows aspects between chart1 planets and chart2 planets: - Chart 1 Planet (with label) - Aspect type - Chart 2 Planet (with label) - Orb (optional) - Applying/Separating (optional)
- property section_name: str
Dignity-related report sections.
Includes: - DignitySection: Essential dignities table - DispositorSection: Dispositor chains and final dispositors
- class stellium.presentation.sections.dignities.DignitySection(essential='both', show_details=False)[source]
Bases:
objectTable of essential dignities for planets.
Shows dignity scores and details for traditional and/or modern systems. Gracefully handles missing dignity data with helpful message.
- generate_data(chart)[source]
Generate dignity table.
For MultiChart/Comparison, shows dignities for each chart grouped by label.
- property section_name: str
- class stellium.presentation.sections.dignities.DispositorSection(mode='both', rulership='traditional', house_system=None, show_chains=True)[source]
Bases:
objectDispositor analysis section.
Shows planetary and/or house-based dispositor chains, final dispositor(s), and mutual receptions. Text summary only - graphviz rendering is separate.
Example
>>> section = DispositorSection(mode="both") >>> data = section.generate_data(chart)
- generate_data(chart)[source]
Generate dispositor analysis.
For MultiChart/Comparison, shows dispositors for each chart grouped by label. Returns a compound section with subsections for planetary and/or house dispositors, each showing final dispositor and mutual receptions.
- property section_name: str
Midpoint-related report sections.
Includes: - MidpointSection: Table of calculated midpoints - MidpointAspectsSection: Planets aspecting midpoints
- class stellium.presentation.sections.midpoints.MidpointAspectsSection(mode='conjunction', orb=1.5, midpoint_filter='all', sort_by='orb')[source]
Bases:
objectTable of planets aspecting midpoints.
This is what most people care about with midpoints: which planets activate which midpoints? Typically conjunctions are most important (1-2° orb), but hard aspects (square, opposition) can also be shown.
Shows: - Planet that aspects the midpoint - Aspect type (conjunction, square, etc.) - Midpoint being aspected (e.g., “Sun/Moon”) - Orb in degrees
- ASPECT_ANGLES = {'Conjunction': 0, 'Opposition': 180, 'Sextile': 60, 'Square': 90, 'Trine': 120}
- CORE_OBJECTS = {'ASC', 'MC', 'Moon', 'Sun'}
- generate_data(chart)[source]
Generate midpoint aspects table.
For MultiChart/Comparison, shows midpoint aspects for each chart grouped by label.
- property section_name: str
- class stellium.presentation.sections.midpoints.MidpointSection(mode='all', threshold=None)[source]
Bases:
objectTable of midpoints.
Shows: - Midpoint pair (e.g., “Sun/Moon”) - Degree position - Sign
- CORE_OBJECTS = {'ASC', 'MC', 'Moon', 'Sun'}
- generate_data(chart)[source]
Generate midpoints table.
For MultiChart/Comparison, shows midpoints for each chart grouped by label.
- property section_name: str
Midpoint Tree visualization section.
Generates tree diagrams showing which midpoints aspect focal points (planets/angles). This is a standard Uranian/Hamburg astrology technique for interpreting planetary pictures.
- Example tree output:
☉ Sun (15°23’ ♑) ├── ☽/♂ ☌ 0.3° Moon/Mars conjunction ├── ♀/♄ □ 1.1° Venus/Saturn square └── ☿/♃ ⊼ 0.8° Mercury/Jupiter semi-square
- class stellium.presentation.sections.midpoint_tree.MidpointBranch(midpoint, midpoint_display, aspect_name, aspect_glyph, orb, midpoint_position)[source]
Bases:
objectA single branch in a midpoint tree (one midpoint aspecting the focal point).
- aspect_glyph: str
- aspect_name: str
- midpoint: MidpointPosition | CelestialPosition
- midpoint_display: str
- midpoint_position: str
- orb: float
- class stellium.presentation.sections.midpoint_tree.MidpointTree(focal_point, focal_display, focal_position, branches)[source]
Bases:
objectA complete midpoint tree for one focal point.
- branches: list[MidpointBranch]
- focal_display: str
- focal_point: CelestialPosition
- focal_position: str
- class stellium.presentation.sections.midpoint_tree.MidpointTreeSection(tree_bases=None, branch_objects=None, orb=1.5, aspect_mode='conjunction', output='both')[source]
Bases:
objectMidpoint Tree visualization section.
Generates tree diagrams showing which midpoints aspect focal points. Standard technique in Uranian/Hamburg astrology for interpreting planetary pictures.
For each focal point (default: Sun, Moon, MC, ASC), shows all midpoints that aspect it within the configured orb.
Example:
section = MidpointTreeSection( tree_bases=["Sun", "Moon", "MC", "ASC"], orb=1.5, aspect_mode="hard", # conjunction + 45° series output="both" )
- ALL_ASPECTS = {'Conjunction': 0, 'Opposition': 180, 'Sextile': 60, 'Square': 90, 'Trine': 120}
- DEFAULT_BRANCH_OBJECTS = ['Sun', 'Moon', 'Mercury', 'Venus', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto', 'ASC', 'MC', 'True Node']
- DEFAULT_TREE_BASES = ['Sun', 'Moon', 'MC', 'ASC']
- HARD_ASPECTS = {'Conjunction': 0, 'Opposition': 180, 'Semisquare': 45, 'Sesquisquare': 135, 'Square': 90}
- property section_name: str
Miscellaneous report sections.
Includes: - CacheInfoSection: Cache statistics - MoonPhaseSection: Moon phase information - DeclinationSection: Planetary declinations - DeclinationAspectSection: Parallel and contraparallel aspects - FixedStarsSection: Fixed star positions - ArabicPartsSection: Arabic Parts (Lots)
- class stellium.presentation.sections.misc.AntisciaSection(include_contra=True, show_points=False)[source]
Bases:
objectTable of Antiscia and Contra-Antiscia conjunctions.
Antiscia are “hidden conjunctions” - when one planet’s reflection point (across the solstice axis) is conjunct another planet. Contra-antiscia are reflections across the equinox axis.
Shows: - The two planets involved - Whether it’s antiscia or contra-antiscia - The orb of the conjunction - Whether the aspect is applying or separating
- property section_name: str
- class stellium.presentation.sections.misc.ArabicPartsSection(mode='all', show_formula=True, show_description=False)[source]
Bases:
objectTable of Arabic Parts (Lots).
Shows calculated Arabic Parts with their positions, house placements, and optionally their formulas and descriptions.
Modes: - “all”: All calculated parts - “core”: 7 Hermetic Lots (Fortune, Spirit, Eros, Necessity, Courage, Victory, Nemesis) - “family”: Family & Relationship Lots (Father, Mother, Marriage, Children, Siblings) - “life”: Life Topic Lots (Action, Profession, Passion, Illness, Death, etc.) - “planetary”: Planetary Exaltation Lots (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn)
- CORE_PARTS = {'Part of Courage (Tolma)', 'Part of Eros (Love)', 'Part of Eros (Planetary)', 'Part of Fortune', 'Part of Necessity (Ananke)', 'Part of Nemesis', 'Part of Spirit', 'Part of Victory (Nike)'}
- FAMILY_PARTS = {'Part of Children', 'Part of Father', 'Part of Marriage', 'Part of Mother', 'Part of Siblings'}
- LIFE_PARTS = {'Part of Action (Praxis)', 'Part of Death', 'Part of Debt / Bondage', 'Part of Friends / Associates', 'Part of Illness / Disease', 'Part of Passion / Lust', 'Part of Profession (User)', 'Part of Travel'}
- PLANETARY_PARTS = {'Part of Jupiter (Exaltation)', 'Part of Mars (Exaltation)', 'Part of Mercury (Exaltation)', 'Part of Saturn (Exaltation)', 'Part of Venus (Exaltation)', 'Part of the Moon (Exaltation)', 'Part of the Sun (Exaltation)'}
- property section_name: str
- class stellium.presentation.sections.misc.CacheInfoSection[source]
Bases:
objectDisplay cache statistics in reports.
- property section_name: str
- class stellium.presentation.sections.misc.DeclinationAspectSection(mode='all', show_orbs=True, show_oob_status=True, sort_by='orb')[source]
Bases:
objectTable of declination aspects (Parallel and Contraparallel).
Shows: - Planet 1 (with glyph) - Aspect type (Parallel ∥ or Contraparallel ⋕) - Planet 2 (with glyph) - Orb (optional) - Out-of-bounds status (if either planet is OOB)
- property section_name: str
- class stellium.presentation.sections.misc.DeclinationSection[source]
Bases:
objectTable of planetary declinations.
Shows: - Planet name with glyph - Declination value (degrees north/south of celestial equator) - Direction (North/South) - Out-of-bounds status
- generate_data(chart)[source]
Generate declination table data.
For MultiChart/Comparison, shows declinations for each chart grouped by label. Shows declination values for all planets with equatorial coordinates. Highlights out-of-bounds planets (beyond Sun’s max declination).
- property section_name: str
- class stellium.presentation.sections.misc.FixedStarsSection(tier=None, include_keywords=True, sort_by='longitude')[source]
Bases:
objectTable of fixed star positions.
Shows: - Star name with glyph - Zodiac position (sign + degree) - Constellation - Magnitude (brightness) - Traditional planetary nature - Keywords
- property section_name: str
- class stellium.presentation.sections.misc.MoonPhaseSection[source]
Bases:
objectDisplay Moon phase information.
- property section_name: str
Timing technique report sections.
Includes: - ProfectionSection: Annual and monthly profections - ZodiacalReleasingSection: Zodiacal releasing periods and timeline
- class stellium.presentation.sections.timing.ProfectionSection(age=None, date=None, include_monthly=True, include_multi_point=True, include_timeline=False, timeline_range=None, points=None, house_system=None, rulership='traditional')[source]
Bases:
objectProfection timing analysis section.
Shows annual profections with Lord of the Year, activated house, and optionally monthly profections and multi-point analysis.
- property section_name: str
- class stellium.presentation.sections.timing.ZodiacalReleasingSection(lots=None, mode='both', query_date=None, query_age=None, context_periods=2)[source]
Bases:
objectZodiacal Releasing timing analysis section.
Shows ZR periods from one or more Lots (Fortune, Spirit, etc.), with options to display current snapshot and/or L1 timeline.
Snapshot mode shows: - Current L1/L2 periods (always shown) - L3/L4 context (current ± 2 periods) for finer timing
Timeline mode shows: - All L1 periods with ages and status indicators - Peak (★), Angular (◆), and Current (⚡) markers
- property section_name: str
Transit calendar report sections.
These sections show sky events (not natal chart analysis): - StationSection: Planetary stations (retrograde/direct) - IngressSection: Sign ingresses - EclipseSection: Solar and lunar eclipses
Unlike other sections, these are date-range based rather than chart-analysis based. The chart is passed for protocol compliance but the sections use their own start/end dates.
- class stellium.presentation.sections.transits.EclipseSection(start, end, eclipse_types='both')[source]
Bases:
objectEclipse report section.
Shows solar and lunar eclipses within a date range. Useful for eclipse calendars and transit planning.
Note: This section uses explicit start/end dates rather than analyzing the natal chart. The chart parameter in generate_data() is accepted for protocol compliance but not used internally.
- generate_data(chart)[source]
Generate eclipse data for the date range.
- Parameters:
chart (
CalculatedChart) – CalculatedChart (accepted for protocol, not used internally)- Return type:
- Returns:
Dictionary with eclipse data for rendering
- property section_name: str
- class stellium.presentation.sections.transits.IngressSection(start, end, planets=None, include_moon=False, include_minor=False)[source]
Bases:
objectSign ingress report section.
Shows when planets enter new zodiac signs within a date range. Useful for tracking sign changes and transit planning.
Note: This section uses explicit start/end dates rather than analyzing the natal chart. The chart parameter in generate_data() is accepted for protocol compliance but not used internally.
- generate_data(chart)[source]
Generate ingress data for the date range.
- Parameters:
chart (
CalculatedChart) – CalculatedChart (accepted for protocol, not used internally)- Return type:
- Returns:
Dictionary with ingress data for rendering
- property section_name: str
- class stellium.presentation.sections.transits.StationSection(start, end, planets=None, include_minor=False)[source]
Bases:
objectPlanetary stations report section.
Shows when planets station retrograde or direct within a date range. Useful for retrograde calendars and transit planning.
Note: This section uses explicit start/end dates rather than analyzing the natal chart. The chart parameter in generate_data() is accepted for protocol compliance but not used internally.
- generate_data(chart)[source]
Generate station data for the date range.
- Parameters:
chart (
CalculatedChart) – CalculatedChart (accepted for protocol, not used internally)- Return type:
- Returns:
Dictionary with station data for rendering
- property section_name: str
Transit period sections for natal chart transit analysis.
Computes when transiting planets form aspects to natal positions, with orb entry/exit windows and multi-pass handling for retrograde transits.
Two output modes: - TransitListSection: plain-text rows, e.g.:
Dec 2 - Mar 2 ‘26 — Jupiter △ natal Chiron Dec 4 — Mercury □ natal Jupiter Jan 8 - Feb 6 ‘26 — Uranus △ natal Neptune (3x: Jan 15, Jan 30, Feb 12)
TransitGanttSection: SVG horizontal bar chart grouped by transiting planet. Rows are aspect events; bars show orb window; tick marks show exact dates.
Both sections take an explicit start/end date range and accept a natal CalculatedChart via generate_data() following the ReportSection protocol.
- class stellium.presentation.sections.transit_periods.TransitGanttSection(start, end, transit_planets=None, aspects=None, include_natal_points=None, width=900, row_height=14, exclude_fast_planets=True, theme='dark')[source]
Bases:
objectNatal transit periods as an SVG Gantt timeline chart.
Each row is a transit event; bars span the orb window; tick marks indicate exact dates. Rows are grouped by transiting planet.
Defaults to excluding fast planets (Sun, Moon, Mercury, Venus, Mars) since their many short transits make the chart unreadable. Use TransitListSection for comprehensive fast-planet output.
Usage:
section = TransitGanttSection( start=datetime(2025, 12, 1), end=datetime(2026, 6, 1), ) report = ReportBuilder().from_chart(natal_chart).add_section(section) report.render(format="pdf")
- generate_data(chart)[source]
Calculate and return transit periods as an SVG Gantt chart.
- Parameters:
chart (
CalculatedChart) – The natal CalculatedChart to transit to.- Return type:
- Returns:
Dict with type=”svg” and SVG content string.
- property section_name: str
- class stellium.presentation.sections.transit_periods.TransitListSection(start, end, transit_planets=None, aspects=None, include_natal_points=None, exclude_fast_planets=False)[source]
Bases:
objectNatal transit aspect periods as a plain-text list.
Shows when transiting planets form aspects to natal positions, with orb entry/exit dates, e.g.:
Dec 2 - Mar 2 '26 — Jupiter △ natal Chiron Dec 4 — Mercury □ natal Jupiter Jan 8 - Feb 6 '26 — Uranus △ natal Neptune (3x: Jan 15, Jan 30, Feb 12)
Usage:
section = TransitListSection( start=datetime(2025, 12, 1), end=datetime(2026, 6, 1), ) report = ReportBuilder().from_chart(natal_chart).add_section(section) report.render()
Note: The chart parameter in generate_data() is the natal chart.
- generate_data(chart)[source]
Calculate and return transit periods as text rows.
- Parameters:
chart (
CalculatedChart) – The natal CalculatedChart to transit to.- Return type:
- Returns:
Dict with type=”text” and structured period data.
- property section_name: str
- class stellium.presentation.sections.transit_periods.TransitPeriod(transit_planet, natal_planet, aspect_name, aspect_angle, orb, exact_dates, start, end)[source]
Bases:
objectA single transit event: one transiting planet forming one aspect to one natal point.
Includes the orb entry/exit window and all exact dates within the window. Multi-pass transits (retrograde causing 2–3 exact crossings) are represented as a single TransitPeriod with multiple exact_dates entries.
- transit_planet
Name of the transiting planet (e.g. “Jupiter”)
- natal_planet
Name of the natal point being aspected (e.g. “Sun”)
- aspect_name
Name of the aspect (e.g. “Trine”)
- aspect_angle
Aspect angle in degrees (e.g. 120.0)
- orb
Orb used for this calculation in degrees
- exact_dates
One or more exact-aspect datetimes (UTC); 2–3 = retrograde passes
- start
When transit entered orb, or None if before the search window
- end
When transit exits orb, or None if extends beyond search window
- aspect_angle: float
- aspect_name: str
- property duration_days: float | None
Duration in days, or None if window extends outside the search range.
- property is_multi_pass: bool
True if the transit crosses the exact point more than once (retrograde).
- natal_planet: str
- orb: float
- property peak_date: datetime
The middle exact date — most representative for sorting.
- transit_planet: str
- stellium.presentation.sections.transit_periods.calculate_transit_periods(natal_chart, start, end, transit_planets=None, aspects=None, include_natal_points=None)[source]
Calculate transit-to-natal aspect periods for a date range.
For each (transiting planet × natal point × aspect) combination, returns TransitPeriod objects with orb entry/exit dates and all exact dates within the window, including multiple passes from retrograde motion.
Reuses stellium.engines.search functions for all ephemeris lookups — no raw Swiss Ephemeris calls here.
- Parameters:
natal_chart (
CalculatedChart) – The natal CalculatedChart to transit to.start (
datetime) – Start of date range (UTC).end (
datetime) – End of date range (UTC).transit_planets (
list[str] |None) – Planets to use as transits (default: all 12).aspects (
dict[str,float] |None) – Dict of {aspect_name: orb_degrees} (default: 5 major).include_natal_points (
list[str] |None) – Limit natal points checked (default: all planets).
- Return type:
list[TransitPeriod]- Returns:
List of TransitPeriod objects sorted by start date (or first exact date if start is outside the search window).
Profection wheel visualization section.
Generates SVG wheel visualizations for annual profections: - Circular wheel with ages 0-95 spiraling through 12 houses - House labels with zodiac signs around perimeter - Natal planet positions marked on the wheel - Current age highlighting - Summary table with profection details
- class stellium.presentation.sections.profection_visualization.ProfectionVisualizationSection(age=None, date=None, compare_ages=None, show_wheel=True, show_table=True, house_system=None, rulership='traditional')[source]
Bases:
objectProfection wheel visualization section.
Generates an SVG visualization showing: - Circular wheel with ages spiraling through 12 houses - Zodiac signs and house labels around the perimeter - Natal planet positions - Current age highlighted - Summary table with profection details
Example
- section = ProfectionVisualizationSection(
age=30, show_table=True
) data = section.generate_data(chart) # data[“content”] contains SVG string
- property section_name: str
- class stellium.presentation.sections.profection_visualization.ProfectionVizConfig(current_age=None, show_wheel=True, show_table=True, compare_ages=None, width=600, colors=<factory>)[source]
Bases:
objectConfiguration for profection wheel visualization.
- colors: dict
- show_table: bool = True
- show_wheel: bool = True
- width: int = 600
Zodiacal Releasing visualization section.
Generates SVG timeline visualizations similar to Honeycomb Collective style: - Page 1: Overview (natal angles chart + period length reference table) - Page 2: Stacked L1/L2/L3 timelines with peak shapes
- class stellium.presentation.sections.zr_visualization.ZRVisualizationSection(lot='Part of Fortune', year=None, start_date=None, end_date=None, levels=(1, 2, 3), highlight_date=None, output='both')[source]
Bases:
objectZodiacal Releasing visualization section.
Generates SVG timeline visualizations in Honeycomb Collective style: - Overview page: natal angles chart + period length reference - Timeline page: stacked L1/L2/L3 timelines with peak shapes
Returns SVG content that can be embedded in PDF planners or reports.
Example
- section = ZRVisualizationSection(
lot=”Part of Fortune”, year=2025, output=”timeline” # or “overview” or “both”
) data = section.generate_data(chart) # data[“content”] contains SVG string
- generate_data(chart)[source]
Generate ZR visualization data.
- property section_name: str
- class stellium.presentation.sections.zr_visualization.ZRVizConfig(year=None, start_date=None, end_date=None, levels=(1, 2, 3), highlight_date=None, show_loosing_bond=True, show_overview=True, show_timeline=True, width=800, colors=<factory>)[source]
Bases:
objectConfiguration for ZR visualization.
- colors: dict
- show_loosing_bond: bool = True
- show_overview: bool = True
- show_timeline: bool = True
- width: int = 800
Visualization (stellium.visualization)¶
Chart rendering and SVG generation.
Core Chart Drawing Engine (stellium.visualization.core)
This module provides the core, refactored drawing system. It is based on a “Layer” strategy pattern.
ChartRenderer: The main “canvas” and coordinate system.
IRenderLayer: The protocol (interface) that all drawable layers must follow.
- class stellium.visualization.core.ChartRenderer(size=600, rotation=0.0, theme=None, style_config=None, zodiac_palette=None, aspect_palette=None, planet_glyph_palette=None, color_sign_info=False)[source]
Bases:
objectThe core chart drawing canvas and coordinate system.
This class holds the SVG drawing object and provides the geometric utilities for layers to draw themselves. It acts as the “Context” in the strategy pattern.
- astrological_to_svg_angle(astro_deg)[source]
Converts astrological degrees (0° = Aries) to SVG degrees (0° = 3 o’clock), appling the chart’s rotation.
Our system: 0° Aries is at 9 o’clock (180° SVG). Rotation is COUNTER-CLOCKWISE.
- Return type:
- class stellium.visualization.core.IRenderLayer(*args, **kwargs)[source]
Bases:
ProtocolProtocol (interface) for all drawable chart layers.
Each layer is a self-contained drawing strategy.
- render(renderer, dwg, chart)[source]
The main drawing method for the layer.
- Parameters:
renderer (
ChartRenderer) – ChartRenderer instance, used to access coordinate methodsdefinitions. ((.polar_to_cartesian) and style/radius)
dwg (
Drawing) – The svgwrite.Drawing object to add elements to.chart (
CalculatedChart) – The full CalculatedChart data object.
- Return type:
- stellium.visualization.core.embed_svg_glyph(dwg, svg_content, x, y, size, fill_color=None)[source]
Embed an SVG glyph inline as a nested SVG element.
This function parses SVG content and embeds it directly into the drawing as a nested <svg> element with proper positioning and scaling. This approach works across all browsers and SVG viewers, unlike external image references.
- Parameters:
dwg (
Drawing) – The svgwrite Drawing to add the element tosvg_content (
str) – The raw SVG content string (from get_glyph())x (
float) – Center x coordinate for the glyphy (
float) – Center y coordinate for the glyphsize (
float) – Desired size (width and height) in pixelsfill_color (
str|None) – Optional color to override stroke/fill (for theming)
- Return type:
- stellium.visualization.core.get_aspect_glyph(aspect_name)[source]
Get the glyph for an astrological aspect.
- stellium.visualization.core.get_display_name(object_name)[source]
Get the display name for a celestial object.
- stellium.visualization.core.get_glyph(object_name)[source]
Get the glyph for a celestial object, with registry lookup and fallback.
- Parameters:
object_name (
str) – Name of the object (e.g., “Sun”, “Mean Apogee”, “ASC”)- Returns:
“type”: “unicode” or “svg”
”value”: glyph string (unicode) or SVG content string (for inline embedding)
- Return type:
Dictionary with
- class stellium.ChartRenderer(size=600, rotation=0.0, theme=None, style_config=None, zodiac_palette=None, aspect_palette=None, planet_glyph_palette=None, color_sign_info=False)[source]
Bases:
objectThe core chart drawing canvas and coordinate system.
This class holds the SVG drawing object and provides the geometric utilities for layers to draw themselves. It acts as the “Context” in the strategy pattern.
- astrological_to_svg_angle(astro_deg)[source]
Converts astrological degrees (0° = Aries) to SVG degrees (0° = 3 o’clock), appling the chart’s rotation.
Our system: 0° Aries is at 9 o’clock (180° SVG). Rotation is COUNTER-CLOCKWISE.
- Return type:
Fluent builder API for chart visualization.
Provides a convenient, discoverable API for creating chart visualizations with presets and easy customization.
- class stellium.visualization.builder.ChartDrawBuilder(chart)[source]
Bases:
objectFluent builder for chart visualization with preset support.
This builder provides a high-level, user-friendly API for creating chart visualizations. It wraps the lower-level draw_chart() function with a fluent interface and convenient presets.
Example:
# Simple preset chart.draw("chart.svg").preset_standard().save() # Custom configuration chart.draw("custom.svg").with_size(800).with_theme("midnight").with_moon_phase( position="top-left", show_label=True ).save() # Comparison charts comparison.draw("synastry.svg").preset_synastry().save()
- preset_detailed()[source]
Detailed preset: Chart with info boxes and moon phase.
Includes chart info (top-left), aspect counts (top-right), element/modality table (bottom-left), chart shape (bottom-right), and auto-positioned moon phase (center when no aspects, bottom-right when aspects present).
Note: Chart shape is automatically hidden at render time when moon phase is positioned in bottom-right to avoid collision.
- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- preset_minimal()[source]
Minimal preset: Just the core chart with no decorations.
- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- preset_standard()[source]
Standard preset: Core chart with moon phase in center.
- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- preset_synastry()[source]
Synastry preset: Optimized for relationship comparison charts.
For Comparison objects, automatically enables bi-wheel layout with: - Inner wheel: chart1 (native/person1) planets - Outer wheel: chart2 (partner/transit) planets - Extended canvas with position table and aspectarian - Chart info for both people
- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- save(to_string=False)[source]
Build and save the chart visualization using the composer.
Only user-specified values are passed to config classes. All other values use the config defaults (single source of truth).
- Return type:
- Returns:
The filename of the saved SVG file
- Raises:
ValueError – If required configuration is missing
- with_adaptive_colors(sign_info=True)[source]
Enable adaptive coloring for sign glyphs in planet info stack.
- Parameters:
sign_info (
bool) – Color sign glyphs in planet info based on zodiac palette- Return type:
ChartDrawBuilder- Returns:
Self for chaining
Note
Zodiac wheel glyphs are always adaptively colored for accessibility. This setting only controls the tiny sign glyphs in planet info stacks.
- with_aspect_counts(position='top-right')[source]
Add aspect counts summary.
- Parameters:
position (
str) – Corner position- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_aspect_palette(palette)[source]
Set the aspect line color palette.
- Parameters:
palette (
str) – Palette name (e.g., “classic”, “dark”, “blues”, “plasma”)- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_chart_info(position='top-left', fields=None)[source]
Add chart information box.
- with_chart_shape(position='bottom-right')[source]
Add chart shape detection display.
- Parameters:
position (
str) – Corner position- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_degree_ticks(enabled=True)[source]
Enable or disable 1-degree tick marks on the zodiac ring.
When enabled, adds small tick marks at every degree (1°-29° within each sign), in addition to the standard 5° and 10° tick marks.
- Parameters:
enabled (
bool) – True to show 1° ticks, False to hide them (default: True)- Return type:
ChartDrawBuilder- Returns:
Self for chaining
Example
chart.draw().with_degree_ticks().save() # Enable detailed ticks chart.draw().with_degree_ticks(False).save() # Explicitly disable
- with_element_modality_table(position='bottom-left')[source]
Add element × modality cross-table.
- Parameters:
position (
str) – Corner position- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_filename(filename)[source]
Set the output filename.
- Parameters:
filename (
str) – Path to save the SVG file- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_header(height=None)[source]
Enable the chart header band.
The header displays native information prominently at the top of the chart: - Single chart: Name, location (with coordinates), datetime, timezone - Biwheel: Two-column layout with chart1 left-aligned, chart2 right-aligned - Synthesis: “Composite: Name1 & Name2” with midpoint info
When header is enabled, the chart canvas becomes taller (a rectangle instead of a square), and the simplified info corner shows only calculation settings (house system, ephemeris).
- Parameters:
height (
int|None) – Optional custom height in pixels (default: 70)- Return type:
ChartDrawBuilder- Returns:
Self for chaining
Example
# Default header chart.draw(“chart.svg”).with_header().save()
# Custom header height chart.draw(“chart.svg”).with_header(height=90).save()
- with_house_systems(systems)[source]
Configure multiple house systems to overlay on the chart.
- Parameters:
systems (
str|list[str]) – House system(s) to display. Can be: - Single system name (e.g., “Placidus”) - List of system names (e.g., [“Placidus”, “Whole Sign”]) - “all” to display all available house systems from the chart- Return type:
ChartDrawBuilder- Returns:
Self for chaining
Example
# Single additional system builder.with_house_systems(“Whole Sign”)
# Multiple systems builder.with_house_systems([“Placidus”, “Koch”, “Whole Sign”])
# All available systems builder.with_house_systems(“all”)
- with_margin(margin)[source]
Set the margin around the chart.
- Parameters:
margin (
int) – Margin in pixels (default: 10)- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_moon_phase(position='center', show_label=True, size=None, label_size=None)[source]
Configure moon phase display.
- Parameters:
position (
str) – Where to place moon (“center”, “top-left”, “top-right”, “bottom-left”, “bottom-right”)show_label (
bool) – Whether to show the phase namesize (
int|None) – Moon radius in pixels (defaults: 60 for center, 30-35 for corners)label_size (
str|None) – Label font size (defaults: “14px” for center, “11px” for corners)
- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_planet_glyph_palette(palette)[source]
Set the planet glyph color palette.
- Parameters:
palette (
str) – Palette name (e.g., “default”, “element”, “chakra”, “rainbow”)- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_planet_ticks(enabled=True)[source]
Enable or disable colored planet position tick marks.
When enabled (default), draws small colored tick marks on the inner edge of the zodiac ring at each planet’s true position. The ticks use the planet’s glyph color. When planets are spread out due to collision detection, the dashed connector line goes from the glyph to the tick.
- Parameters:
enabled (
bool) – True to show planet ticks, False to hide them (default: True)- Return type:
ChartDrawBuilder- Returns:
Self for chaining
Example
chart.draw().with_planet_ticks(False).save() # Disable planet ticks
- with_size(size)[source]
Set the chart size in pixels.
- Parameters:
size (
int) – Chart size (width and height)- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_tables(position='right', show_position_table=True, show_aspectarian=True, show_house_cusps=True, aspectarian_mode='cross_chart', aspectarian_detailed=False, show_object_types=None)[source]
Add extended canvas with position table and/or aspectarian grid.
This enables an extended canvas area (right, left, or below the chart) that can display tabular data like planetary positions and aspect grids.
- Parameters:
position (
str) – Where to place the extended canvas (“right”, “left”, or “below”)show_position_table (
bool) – Show planetary position tableshow_aspectarian (
bool) – Show aspectarian gridshow_house_cusps (
bool) – Show house cusp table (natal charts only)aspectarian_mode (
str) – For comparison charts, which aspects to show: - “cross_chart”: Only cross-chart aspects (default) - “all”: All three grids (chart1 internal, chart2 internal, cross-chart) - “chart1”: Only chart1 internal aspects - “chart2”: Only chart2 internal aspectsaspectarian_detailed (
bool) – If True, show orb and A/S (applying/separating) indicator in each cell. If False (default), show larger glyphs only.show_object_types (
list[str] |None) – List of object types to include in tables. If None, uses default (planet, asteroid, point, node, angle). Example values:["planet", "asteroid", "midpoint"]or["planet", "asteroid", "point", "node", "angle", "arabic_part"]
- Return type:
ChartDrawBuilder- Returns:
Self for chaining
Example:
# Standard extended canvas builder.with_tables(position="right") # Position table only builder.with_tables(position="right", show_aspectarian=False) # With house cusps table (natal charts) builder.with_tables(position="right", show_house_cusps=True) # Custom aspectarian mode for synastry builder.with_tables(position="right", aspectarian_mode="all") # Detailed aspectarian with orb and applying/separating builder.with_tables(position="right", aspectarian_detailed=True) # Include midpoints and Arabic parts in tables builder.with_tables( position="right", show_object_types=["planet", "asteroid", "midpoint", "arabic_part"] )
- with_theme(theme)[source]
Set the chart theme.
- Parameters:
theme (
str) – Theme name (e.g., “classic”, “dark”, “midnight”, “neon”, “celestial”)- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- with_zodiac_palette(palette)[source]
Set the zodiac ring color palette.
- Parameters:
palette (
str|bool) – Can be: - True: Use theme’s default colorful palette - str: Specific palette name (e.g., “grey”, “rainbow”, “viridis”, “elemental”)- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- Usage:
# Default (no call): Monochrome using theme’s zodiac ring_color
.with_zodiac_palette(True) # Use theme’s colorful default palette .with_zodiac_palette(“rainbow”) # Use specific rainbow palette .with_zodiac_palette(“grey”) # Monochrome grey palette
- without_header()[source]
Disable the chart header band.
When header is disabled, all native info (name, location, datetime, etc.) is displayed in the chart info corner instead.
- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- without_moon_phase()[source]
Disable moon phase display.
- Return type:
ChartDrawBuilder- Returns:
Self for chaining
- without_tables()[source]
Disable extended canvas tables.
- Return type:
ChartDrawBuilder- Returns:
Self for chaining
Chart Themes (stellium.visualization.themes)
Defines complete visual themes for chart rendering, including colors, line styles, and default zodiac palettes.
- class stellium.visualization.themes.ChartTheme(value)[source]
Bases:
StrEnumAvailable visual themes for chart rendering.
- ATLAS = 'atlas'
- CELESTIAL = 'celestial'
- CIVIDIS = 'cividis'
- CLASSIC = 'classic'
- DARK = 'dark'
- INFERNO = 'inferno'
- MAGMA = 'magma'
- MIDNIGHT = 'midnight'
- NEON = 'neon'
- PASTEL = 'pastel'
- PLASMA = 'plasma'
- SEPIA = 'sepia'
- TURBO = 'turbo'
- VIRIDIS = 'viridis'
- stellium.visualization.themes.get_theme_default_aspect_palette(theme)[source]
Get the default aspect palette for a theme.
- Parameters:
theme (
ChartTheme) – The theme- Return type:
AspectPalette- Returns:
Default AspectPalette for this theme
- stellium.visualization.themes.get_theme_default_palette(theme)[source]
Get the default zodiac palette for a theme.
- Parameters:
theme (
ChartTheme) – The theme- Return type:
ZodiacPalette- Returns:
Default ZodiacPalette for this theme
- stellium.visualization.themes.get_theme_default_planet_palette(theme)[source]
Get the default planet glyph palette for a theme.
- Parameters:
theme (
ChartTheme) – The theme- Return type:
PlanetGlyphPalette- Returns:
Default PlanetGlyphPalette for this theme
- stellium.visualization.themes.get_theme_description(theme)[source]
Get a human-readable description of a theme.
- Parameters:
theme (
ChartTheme) – The theme to describe- Return type:
- Returns:
Description string
- stellium.visualization.themes.get_theme_style(theme)[source]
Get the complete style configuration for a theme.
Zodiac Color Palettes (stellium.visualization.palettes)
Defines color schemes for the zodiac wheel visualization, aspect lines, planet glyphs, and color utilities for adaptive theming.
- class stellium.visualization.palettes.AspectPalette(value)[source]
Bases:
StrEnumAvailable color palettes for aspect lines.
- BLUES = 'blues'
- CELESTIAL = 'celestial'
- CIVIDIS = 'cividis'
- CLASSIC = 'classic'
- DARK = 'dark'
- EARTH_TONES = 'earth_tones'
- GREYSCALE = 'greyscale'
- INFERNO = 'inferno'
- MAGMA = 'magma'
- MIDNIGHT = 'midnight'
- NEON = 'neon'
- PASTEL = 'pastel'
- PLASMA = 'plasma'
- PURPLES = 'purples'
- SEPIA = 'sepia'
- TURBO = 'turbo'
- VIRIDIS = 'viridis'
- class stellium.visualization.palettes.PlanetGlyphPalette(value)[source]
Bases:
StrEnumAvailable color palettes for planet glyphs.
- CHAKRA = 'chakra'
- DEFAULT = 'default'
- ELEMENT = 'element'
- INFERNO = 'inferno'
- LUMINARIES = 'luminaries'
- PLANET_TYPE = 'planet_type'
- PLASMA = 'plasma'
- RAINBOW = 'rainbow'
- SIGN_RULER = 'sign_ruler'
- TURBO = 'turbo'
- VIRIDIS = 'viridis'
- class stellium.visualization.palettes.ZodiacPalette(value)[source]
Bases:
StrEnumAvailable color palettes for the zodiac wheel.
- CARDINALITY = 'cardinality'
- CIVIDIS = 'cividis'
- COOLWARM = 'coolwarm'
- ELEMENTAL = 'elemental'
- ELEMENTAL_DARK = 'elemental_dark'
- ELEMENTAL_MIDNIGHT = 'elemental_midnight'
- ELEMENTAL_NEON = 'elemental_neon'
- ELEMENTAL_SEPIA = 'elemental_sepia'
- GREY = 'grey'
- INFERNO = 'inferno'
- MAGMA = 'magma'
- PLASMA = 'plasma'
- RAINBOW = 'rainbow'
- RAINBOW_CELESTIAL = 'rainbow_celestial'
- RAINBOW_DARK = 'rainbow_dark'
- RAINBOW_MIDNIGHT = 'rainbow_midnight'
- RAINBOW_NEON = 'rainbow_neon'
- RAINBOW_SEPIA = 'rainbow_sepia'
- SPECTRAL = 'spectral'
- TURBO = 'turbo'
- VIRIDIS = 'viridis'
- stellium.visualization.palettes.adjust_color_for_contrast(original_color, background_color, min_contrast=4.5, max_iterations=20)[source]
Adjust a color to ensure minimum contrast against a background.
This algorithm: 1. Checks if original color already has sufficient contrast 2. If not, determines if background is light or dark 3. Adjusts the color’s lightness/darkness in the opposite direction 4. Iterates until minimum contrast is achieved
- Parameters:
- Return type:
- Returns:
Adjusted hex color that meets minimum contrast
- stellium.visualization.palettes.build_aspect_styles_from_palette(palette)[source]
Build complete aspect styling dict with palette colors + registry line styles.
This merges palette colors with the ASPECT_REGISTRY’s line_width and dash_pattern, ensuring themes only change colors while preserving the registry’s line styling.
- stellium.visualization.palettes.get_aspect_palette_colors(palette)[source]
Get aspect colors for a given palette.
Returns a dictionary mapping aspect names to hex colors. Includes Conjunction, Sextile, Square, Trine, Opposition, and minor aspects (Semisextile, Semisquare, Sesquisquare, Quincunx). Results are cached in memory for performance.
- stellium.visualization.palettes.get_aspect_palette_description(palette)[source]
Get a human-readable description of an aspect palette.
- Parameters:
palette (
AspectPalette) – The palette to describe- Return type:
- Returns:
Description string
- stellium.visualization.palettes.get_contrast_ratio(color1, color2)[source]
Calculate the contrast ratio between two colors.
- stellium.visualization.palettes.get_luminance(hex_color)[source]
Calculate the relative luminance of a color.
Uses WCAG formula for luminance calculation.
- stellium.visualization.palettes.get_palette_colors(palette)[source]
Get the color list for a zodiac wheel palette.
Returns a list of 12 colors (one per sign, starting with Aries). Results are cached in memory for performance.
Special case: If palette is a string starting with “single_color:”, extracts the hex color and returns 12 copies of it for a monochrome wheel.
- stellium.visualization.palettes.get_palette_description(palette)[source]
Get a human-readable description of a palette.
- Parameters:
palette (
ZodiacPalette) – The palette to describe- Return type:
- Returns:
Description string
- stellium.visualization.palettes.get_planet_glyph_color(planet_name, palette, theme_default_color='#222222')[source]
Get the color for a planet glyph based on palette.
- stellium.visualization.palettes.get_planet_glyph_palette_description(palette)[source]
Get a human-readable description of a planet glyph palette.
- Parameters:
palette (
PlanetGlyphPalette) – The palette to describe- Return type:
- Returns:
Description string
- stellium.visualization.palettes.get_sign_info_color(sign_index, zodiac_palette, background_color, min_contrast=4.5)[source]
Get an adaptive color for sign glyph in planet info stack.
This function: 1. Gets the sign’s zodiac wheel color from the palette 2. Adjusts it for contrast against the background 3. Returns a color that’s readable while maintaining zodiac color story
- Parameters:
- Return type:
- Returns:
Hex color for the sign glyph that contrasts with background
- stellium.visualization.palettes.hex_to_rgb(hex_color)[source]
Convert hex color to RGB tuple.
- stellium.visualization.palettes.rgb_to_hex(r, g, b)[source]
Convert RGB values to hex color string.
- class stellium.visualization.composer.ChartComposer(config)[source]
Bases:
objectMain orchestrator for chart visualization.
This is the new public API that replaces draw_chart() and draw_comparison_chart().
- class stellium.visualization.config.ChartVisualizationConfig(wheel, corners, tables, header=None, base_size=600, filename='chart.svg', auto_center=True, auto_grow_wheel=False, min_margin=10)[source]
Bases:
objectComplete configuration for chart visualization.
- auto_center: bool = True
- auto_grow_wheel: bool = False
- base_size: int = 600
- corners: InfoCornerConfig
- filename: str = 'chart.svg'
- header: HeaderConfig = None
- min_margin: int = 10
- tables: TableConfig
- wheel: ChartWheelConfig
- class stellium.visualization.config.ChartWheelConfig(chart_type, house_systems=None, single_radii=<factory>, biwheel_radii=<factory>, multiwheel_2_radii=<factory>, multiwheel_3_radii=<factory>, multiwheel_4_radii=<factory>, theme=None, zodiac_palette=None, aspect_palette=None, planet_glyph_palette=None, color_sign_info=False, show_degree_ticks=False, show_planet_ticks=True, multiwheel_glyph_sizes=<factory>, multiwheel_info_distances=<factory>, multiwheel_canvas_scales=<factory>)[source]
Bases:
objectConfiguration for the main chart wheel.
- chart_type: Literal['single', 'biwheel', 'multiwheel']
- color_sign_info: bool = False
- get_multiwheel_canvas_scale(chart_count)[source]
Get the canvas scale factor for a multiwheel with N charts.
- get_multiwheel_radii(chart_count)[source]
Get the appropriate radii config for a multiwheel with N charts.
- Parameters:
chart_count (
int) – Number of charts (2, 3, or 4)- Return type:
- Returns:
Radii dictionary for the specified chart count
- Raises:
ValueError – If chart_count is not 2, 3, or 4
- show_degree_ticks: bool = False
- show_planet_ticks: bool = True
- theme: ChartTheme | None = None
- class stellium.visualization.config.Dimensions(width, height)[source]
Bases:
objectRepresents width and height.
- height: float
- width: float
- class stellium.visualization.config.HeaderConfig(enabled=True, height=70, name_font_size='18px', name_font_family='Cinzel, serif', details_font_size='12px', line_height=16, coord_precision=4)[source]
Bases:
objectConfiguration for the chart header band.
- coord_precision: int = 4
- details_font_size: str = '12px'
- enabled: bool = True
- height: int = 70
- line_height: int = 16
- name_font_family: str = 'Cinzel, serif'
- name_font_size: str = '18px'
- class stellium.visualization.config.InfoCornerConfig(chart_info=True, chart_info_position='top-left', chart_info_fields=None, aspect_counts=False, aspect_counts_position='top-right', element_modality=False, element_modality_position='bottom-left', chart_shape=False, chart_shape_position='bottom-right', moon_phase=True, moon_phase_position='bottom-right', moon_phase_show_label=True, moon_phase_size=None, moon_phase_label_size=None)[source]
Bases:
objectConfiguration for the 4 info corners (now simplified when header is enabled).
- aspect_counts: bool = False
- aspect_counts_position: str = 'top-right'
- chart_info: bool = True
- chart_info_position: Literal['top-left', 'top-right', 'bottom-left', 'bottom-right'] = 'top-left'
- chart_shape: bool = False
- chart_shape_position: str = 'bottom-right'
- element_modality: bool = False
- element_modality_position: str = 'bottom-left'
- moon_phase: bool = True
- moon_phase_show_label: bool = True
- class stellium.visualization.config.TableConfig(enabled=False, placement='right', show_positions=True, show_houses=True, show_aspectarian=True, aspectarian_mode='cross_chart', aspectarian_detailed=False, padding=10, gap_between_tables=20, gap_between_columns=5, position_col_widths=<factory>, house_col_widths=<factory>, aspectarian_cell_size=24, object_types=None)[source]
Bases:
objectConfiguration for extended tables.
- aspectarian_cell_size: int = 24
- aspectarian_detailed: bool = False
- aspectarian_mode: str = 'cross_chart'
- enabled: bool = False
- gap_between_columns: int = 5
- gap_between_tables: int = 20
- padding: int = 10
- placement: Literal['right', 'left', 'below'] = 'right'
- show_aspectarian: bool = True
- show_houses: bool = True
- show_positions: bool = True
Layer factory for creating visualization layers based on configuration.
This factory encapsulates the logic for creating the right layers in the right order based on the user’s configuration.
- class stellium.visualization.layer_factory.IRenderLayer(*args, **kwargs)[source]
Bases:
ProtocolProtocol for render layers.
- class stellium.visualization.layer_factory.LayerFactory(config)[source]
Bases:
objectCreates visualization layers based on configuration.
This encapsulates all the logic for determining which layers to create, in what order, with what settings - based on the ChartVisualizationConfig.
- create_layers(chart, layout)[source]
Create all configured layers for this chart.
Layers are returned in render order (bottom to top).
- Parameters:
chart (
CalculatedChart|Comparison|MultiWheel|MultiChart) – The chart to visualize (single, comparison, multiwheel, or multichart)layout (
LayoutResult) – The calculated layout (for positioning info)
- Return type:
list[IRenderLayer]- Returns:
List of layers ready to render
Concrete Render Layers (stellium.visualization.layers)
These are the concrete implementations of the IRenderLayer protocol. Each class knows how to draw one specific part of a chart, reading its data from the CalculatedChart object.
This package is organized into submodules by layer type: - chart_frame: Header, borders, and ring boundaries - zodiac: Zodiac ring with signs and degrees - houses: House cusp rendering (inner and outer) - angles: Angle markers (ASC, MC, DSC, IC) - planets: Planet glyphs and positions - aspects: Aspect lines and patterns - info_corners: Chart info, aspect counts, element/modality tables
All layers are re-exported from this __init__ for backward compatibility.
- class stellium.visualization.layers.AngleLayer(style_override=None, wheel_index=0, chart=None)[source]
Bases:
objectRenders the primary chart angles (ASC, MC, DSC, IC).
For multiwheel charts, use wheel_index to specify which chart’s angles to render. Typically only wheel_index=0 (innermost chart) has meaningful angles since transit/progressed charts use the natal houses.
- class stellium.visualization.layers.AspectCountsLayer(position='top-right', style_override=None)[source]
Bases:
objectRenders aspect counts summary in a corner of the chart.
Displays count of each aspect type with glyphs.
- DEFAULT_STYLE = {'font_weight': 'normal', 'line_height': 14, 'text_color': '#333333', 'text_size': '11px', 'title_weight': 'bold'}
- class stellium.visualization.layers.AspectLayer(style_override=None)[source]
Bases:
objectRenders the aspect lines within the chart.
- class stellium.visualization.layers.ChartInfoLayer(position='top-left', fields=None, style_override=None, house_systems=None, header_enabled=False)[source]
Bases:
objectRenders chart metadata information in a corner of the chart.
When header is disabled: displays all native info (name, location, datetime, etc.) When header is enabled: displays only calculation settings (house system, ephemeris)
- CALCULATION_FIELDS = {'ephemeris', 'house_system'}
- DEFAULT_STYLE = {'font_weight': 'normal', 'line_height': 14, 'name_size': '16px', 'name_weight': 'bold', 'text_color': '#333333', 'text_size': '11px'}
- NATIVE_INFO_FIELDS = {'coordinates', 'datetime', 'location', 'name', 'timezone'}
- class stellium.visualization.layers.ChartShapeLayer(position='bottom-right', style_override=None)[source]
Bases:
objectRenders chart shape information in a corner.
Displays the overall pattern/distribution of planets (Bundle, Bowl, Bucket, etc.).
- DEFAULT_STYLE = {'font_weight': 'normal', 'line_height': 14, 'text_color': '#333333', 'text_size': '11px', 'title_weight': 'bold'}
- class stellium.visualization.layers.ElementModalityTableLayer(position='bottom-left', style_override=None)[source]
Bases:
objectRenders element × modality cross-table in a corner.
Shows distribution of planets across elements (Fire, Earth, Air, Water) and modalities (Cardinal, Fixed, Mutable).
- DEFAULT_STYLE = {'col_width': 28, 'font_weight': 'normal', 'line_height': 13, 'text_color': '#333333', 'text_size': '10px', 'title_weight': 'bold'}
- ELEMENT_SYMBOLS = {'Air': '🜁', 'Earth': '🜃', 'Fire': '🜂', 'Water': '🜄'}
- class stellium.visualization.layers.HeaderLayer(height=70, name_font_size='18px', name_font_family="Baskerville, 'Libre Baskerville', Georgia, serif", name_font_weight='600', name_font_style='italic', details_font_size='12px', line_height=16, coord_precision=4)[source]
Bases:
objectRenders the chart header band at the top of the canvas.
Displays native information prominently: - Single chart: Name, location, datetime, timezone, coordinates - Biwheel: Two-column layout with chart1 info left-aligned, chart2 right-aligned - Synthesis: “Composite: Name1 & Name2” or “Davison: Name1 & Name2” with midpoint info
The header uses Baskerville italic-semibold for names (elegant, classical feel) and the normal text font for details.
- class stellium.visualization.layers.HouseCuspLayer(house_system_name, style_override=None, wheel_index=0, chart=None)[source]
Bases:
objectRenders a single set of house cusps and numbers.
To draw multiple systems, add multiple layers.
For multiwheel charts, use wheel_index to specify which chart ring to render: - wheel_index=0: Chart 1 (innermost) - wheel_index=1: Chart 2 - wheel_index=2: Chart 3 - wheel_index=3: Chart 4 (outermost, just inside zodiac)
The layer will look up radii from the renderer using keys like: - chart{N}_ring_outer, chart{N}_ring_inner (ring bounds) - chart{N}_house_number (number placement)
And fill colors from theme: - chart{N}_fill_1, chart{N}_fill_2 (alternating fills)
- class stellium.visualization.layers.MoonRangeLayer(arc_color=None, arc_opacity=0.4)[source]
Bases:
objectRenders a shaded arc showing the Moon’s possible position range.
Used for unknown birth time charts where the Moon could be anywhere within a ~12-14° range throughout the day.
The arc is drawn as a semi-transparent wedge from the day-start position to the day-end position, with the Moon glyph at the noon position.
- class stellium.visualization.layers.MultiWheelAspectLayer(style_override=None)[source]
Bases:
objectRenders cross-chart aspect lines for MultiWheel charts.
Only used for 2-chart multiwheels (biwheels), where showing aspects between the two charts is useful and not too cluttered. For 3-4 chart multiwheels, aspect lines are omitted due to visual complexity.
- class stellium.visualization.layers.OuterAngleLayer(style_override=None)[source]
Bases:
objectRenders the outer wheel angles (for comparison charts).
Deprecated since version Use: AngleLayer(wheel_index=1) instead. This class renders outside the zodiac ring (legacy biwheel style), while the new multiwheel system renders all charts inside the zodiac ring.
- class stellium.visualization.layers.OuterBorderLayer[source]
Bases:
objectRenders the outer containment border for comparison/biwheel charts.
- class stellium.visualization.layers.OuterHouseCuspLayer(house_system_name, style_override=None)[source]
Bases:
objectRenders house cusps for the OUTER wheel (chart2 in comparisons).
This draws house cusp lines and numbers outside the zodiac ring, with a distinct visual style from the inner chart’s houses.
Deprecated since version Use: HouseCuspLayer(wheel_index=1) instead. This class renders outside the zodiac ring (legacy biwheel style), while the new multiwheel system renders all charts inside the zodiac ring.
- class stellium.visualization.layers.PlanetLayer(planet_set, radius_key='planet_ring', style_override=None, use_outer_wheel_color=False, info_stack_direction='inward', show_info_stack=True, show_position_ticks=False, wheel_index=0, info_mode='full', info_stack_distance=0.8, glyph_size_override=None)[source]
Bases:
objectRenders a set of planets at a specific radius.
For multiwheel charts, use wheel_index to specify which chart ring to render: - wheel_index=0: Chart 1 (innermost) - wheel_index=1: Chart 2 - wheel_index=2: Chart 3 - wheel_index=3: Chart 4 (outermost, just inside zodiac)
The info_mode parameter controls how much detail to show: - “full”: Degree + sign glyph + minutes (default for single charts) - “compact”: Degree only, e.g. “15°” (good for multiwheel) - “no_sign”: Degree + minutes, no sign glyph, e.g. “15°32’” - “none”: No info stack, glyph only
- class stellium.visualization.layers.RingBoundaryLayer(chart_count=2, style_override=None)[source]
Bases:
objectRenders circular boundary lines between chart rings in a multiwheel chart.
Draws circles at the boundaries between: - Each chart ring (chart1_ring_outer, chart2_ring_outer, etc.) - The outermost chart and the zodiac ring (zodiac_ring_inner)
Uses the theme’s ring_border styling for color and width.
- class stellium.visualization.layers.ZodiacLayer(palette=ZodiacPalette.GREY, style_override=None, show_degree_ticks=False)[source]
Bases:
objectRenders the outer Zodiac ring, including glyphs and tick marks.
Base imports and utilities for render layers.
This module provides shared imports and utilities used across all layer modules.
- class stellium.visualization.layers.base.Any(*args, **kwargs)[source]
Bases:
objectSpecial type indicating an unconstrained type.
Any is compatible with every type.
Any assumed to have all methods.
All values assumed to be instances of Any.
Note that all the above statements are true from the point of view of static type checkers. At runtime, Any should not be used with instance checks.
- class stellium.visualization.layers.base.AspectPalette(value)[source]
Bases:
StrEnumAvailable color palettes for aspect lines.
- BLUES = 'blues'
- CELESTIAL = 'celestial'
- CIVIDIS = 'cividis'
- CLASSIC = 'classic'
- DARK = 'dark'
- EARTH_TONES = 'earth_tones'
- GREYSCALE = 'greyscale'
- INFERNO = 'inferno'
- MAGMA = 'magma'
- MIDNIGHT = 'midnight'
- NEON = 'neon'
- PASTEL = 'pastel'
- PLASMA = 'plasma'
- PURPLES = 'purples'
- SEPIA = 'sepia'
- TURBO = 'turbo'
- VIRIDIS = 'viridis'
- class stellium.visualization.layers.base.CalculatedChart(datetime, location, positions, house_systems=<factory>, house_placements=<factory>, aspects=(), declination_aspects=(), metadata=<factory>, zodiac_type=None, ayanamsa=None, ayanamsa_value=None, calculation_timestamp=<factory>, chart_tags=())[source]
Bases:
objectComplete calculated chart - the final output.
This is what a ChartBuilder returns. It’s immutable and contains everything you need to analyze or visualize the chart.
- available_components()[source]
List all components and analyzers whose results are available.
- Return type:
- Returns:
Sorted list of component/analyzer names that can be passed to get_component_result().
Examples:
chart.available_components() # ['Arabic Parts', 'Aspect Patterns', 'Essential Dignities']
- bazi()[source]
Calculate the BaZi (Four Pillars / 八字) chart for this birth data.
Uses the chart’s datetime and location to compute the Chinese Four Pillars, reusing the already-resolved timezone information.
- Returns:
A BaZiChart with all four pillars, ready for analysis.
Example:
chart = ChartBuilder.from_details("1994-01-06 11:47", "Palo Alto, CA").calculate() bazi = chart.bazi() print(bazi.hanzi) # Eight characters print(bazi.day_master) # Day Master stem print(bazi.strength()) # Strength analysis
- calculation_timestamp: datetime
- datetime: ChartDateTime
- property default_house_system: str
- draconic()[source]
Return a draconic version of this chart.
The draconic chart rotates all positions so that the North Node is at 0° Aries. This is sometimes called the “soul chart” and represents the soul’s orientation before incarnation.
The transformation subtracts the North Node’s longitude from all positions and normalizes to 0-360°.
- Return type:
- Returns:
A new CalculatedChart with all longitudes rotated and “draconic” added to chart_tags.
Example:
draconic = chart.draconic() print(draconic.get_object("Sun").sign_position) draconic.draw("draconic.svg").save()
- draw(filename='chart.svg')[source]
Start building a chart visualization with fluent API.
This is a convenience method that creates a ChartDrawBuilder for easy, discoverable chart visualization. It provides presets and a fluent interface for customization.
- Parameters:
filename (
str) – Output filename for the SVG- Return type:
ChartDrawBuilder- Returns:
ChartDrawBuilder instance for chaining
Example:
# Simple preset chart.draw("my_chart.svg").preset_standard().save() # Custom configuration chart.draw("custom.svg").with_theme("dark").with_moon_phase( position="top-left", show_label=True ).with_chart_info(position="top-right").save()
- draw_dial(filename='dial.svg', degrees=90)[source]
Draw a Uranian/Hamburg school dial chart.
Creates a dial visualization that compresses the zodiac to reveal hard aspects. On a 90° dial, conjunctions, squares, and oppositions all appear as conjunctions.
- Parameters:
- Return type:
DialDrawBuilder- Returns:
DialDrawBuilder instance for chaining
Example:
# Basic 90° dial chart.draw_dial("dial.svg").save() # With theme chart.draw_dial("dial.svg").with_theme("midnight").save() # With transits on outer ring chart.draw_dial("dial.svg") .with_outer_ring(transit_chart.get_planets(), label="Transits") .save() # 360° dial with pointer to Sun chart.draw_dial("dial.svg", degrees=360) .with_pointer(pointing_to="Sun") .save()
- draw_vedic(filename='vedic_chart.svg', style='north_indian', theme='classic', label_style='abbreviation', show_degrees=True, size=500)[source]
Draw a Vedic (Jyotish) chart in North Indian or South Indian style.
- Parameters:
filename (
str) – Output filename for the SVGstyle (
str) – “north_indian” (diamond) or “south_indian” (grid)theme (
str) – “classic”, “dark”, or “traditional”label_style (
str) – “abbreviation” (Ari, Su), “number” (1, 2), “glyph” (unicode symbols), or “full” (Aries, Sun)show_degrees (
bool) – Show degree + minutes for each planetsize (
int) – SVG width/height in pixels
- Return type:
- Returns:
self (for chaining)
Example:
chart.draw_vedic("north.svg", style="north_indian") chart.draw_vedic("south.svg", style="south_indian", theme="traditional")
- get_accidental_dignities(system=None)[source]
Get accidental dignity calculations.
- get_all_accidental_dignities()[source]
Get all accidental dignities (entire object).
- get_angles()[source]
Get all chart angles.
- Return type:
- get_component_result(name)[source]
Get the result of a component or analyzer by name.
Works with any component added via .add_component() or analyzer added via .add_analyzer() on ChartBuilder.
- Parameters:
name (
str) – The component or analyzer name (e.g., “Arabic Parts”, “Essential Dignities”, “Aspect Patterns”). Also accepts the metadata key as an alias (e.g., “dignities”).- Returns:
list of CelestialPosition objects - For metadata-based components/analyzers: the stored dict or list - For dual-storage components: dict with “positions” and “metadata” keys
- Return type:
For position-based components
- Raises:
KeyError – If no component with that name was used. The error message lists all available component names.
Examples:
parts = chart.get_component_result("Arabic Parts") dignities = chart.get_component_result("Essential Dignities") patterns = chart.get_component_result("Aspect Patterns") chart.available_components()
- get_contraparallels()[source]
Get all contraparallel aspects (same declination, opposite hemispheres).
- get_declination_aspects(aspect_type=None)[source]
Get declination aspects (Parallel and Contraparallel).
- get_dignities(system='traditional')[source]
Get essential dignity calculations.
- get_house(object_name, system_name=None)[source]
Helper method to get the house number for a specific object in a specific system.
- get_houses(system_name=None)[source]
Get all cusps for a specific system (or default system).
- Return type:
- get_mutual_receptions(system='traditional')[source]
Get all mutual receptions in the chart.
- get_nodes()[source]
Get all nodes (True Node, South Node, etc.).
- Return type:
- get_object(name)[source]
Get a celestial object by name.
- Return type:
- get_parallels()[source]
Get all parallel aspects (same declination, same hemisphere).
- get_planet_accidental(planet_name, system=None)[source]
Get accidental dignity for a specific planet.
- get_planet_dignity(planet_name, system='traditional')[source]
Get dignity calculation for a specific planet.
- get_planet_total_score(planet_name, essential_system='traditional', accidental_system=None)[source]
Get combined essential + accidental dignity score.
- get_planets()[source]
Get all planetary objects.
- Return type:
- get_points()[source]
Get all calculated points (Vertex, Lilith, etc.).
- Return type:
- get_strongest_planet(system='traditional')[source]
Find the planet with the highest dignity score (Almuten).
- house_systems: dict[str, HouseCusps]
- location: ChartLocation
- lord_of_year(age, point='ASC', house_system=None, rulership='traditional')[source]
Quick access to Lord of the Year.
The Lord of the Year is the planet ruling the profected sign for a given age. It’s one of the most important predictive indicators in Hellenistic astrology.
- Parameters:
- Return type:
- Returns:
Name of the ruling planet
Example:
print(chart.lord_of_year(30)) # "Saturn"
- positions: tuple[CelestialPosition, ...]
- profection(age=None, date=None, point='ASC', include_monthly=True, house_system=None, rulership='traditional')[source]
Calculate profections for this chart.
Profections move a point forward one sign per year. The planet ruling the profected sign becomes the “Lord of the Year.”
- Parameters:
age (
int|None) – Age in completed years (either age OR date required)date (
datetime|str|None) – Specific date (datetime or ISO string)point (
str) – Point to profect (default “ASC”)include_monthly (
bool) – Whether to include monthly profection (date only)house_system (
str|None) – House system to use (default: prefers Whole Sign)rulership (
str) – “traditional” or “modern” rulers
- Returns:
ProfectionResult, or tuple(annual, monthly) if include_monthly
Example:
# By age result = chart.profection(age=30) print(f"Lord of Year 30: {result.ruler}") # By date (gets both annual and monthly) annual, monthly = chart.profection(date="2025-06-15") print(f"Annual: {annual.ruler}, Monthly: {monthly.ruler}")
- profection_timeline(start_age, end_age, point='ASC', house_system=None, rulership='traditional')[source]
Generate profections for a range of ages.
- Parameters:
- Returns:
ProfectionTimeline with all entries
Example:
timeline = chart.profection_timeline(25, 35) for entry in timeline.entries: print(f"Age {entry.units}: {entry.ruler}")
- profections(age=None, date=None, points=None, house_system=None, rulership='traditional')[source]
Profect multiple points at once.
- Parameters:
- Returns:
MultiProfectionResult with all profections
Example:
result = chart.profections(age=30) print(result.lords) # {"ASC": "Saturn", "Sun": "Mars", ...}
- sect()[source]
Check which sect this chart is (day or night) (Sun above the horizon).
- to_dict()[source]
Serialize to dictionary for JSON export.
This enables web API integration, storage, etc.
- to_prompt_text(sections=None, include_extras=True)[source]
Export chart data as clean, human-readable text suitable for LLM prompts.
Produces a structured markdown-style summary of the chart including planetary positions, house cusps, aspects, declination aspects, and any component results (Arabic Parts, midpoints, fixed stars, dignities, antiscia, aspect patterns, etc.).
- Parameters:
sections (
set[str] |None) – Set of section names to include. WhenNone(the default), every section that has data is included. Valid names: “info”, “positions”, “angles”, “houses”, “aspects”, “declination_aspects”, “arabic_parts”, “midpoints”, “fixed_stars”, “dignities”, “antiscia”, “patterns”, “nodes”, “points”, “extras”.include_extras (
bool) – When True (default), automatically picks up data from unknown/future components that aren’t handled by a dedicated section. Set to False to only show data from known, explicitly-supported sections.
- Return type:
- Returns:
A multi-line string ready to paste into an LLM prompt.
Example:
text = chart.to_prompt_text() prompt = f"Interpret this birth chart:\n\n{text}" # Only specific sections text = chart.to_prompt_text(sections={"info", "positions", "aspects"})
- voc_moon(aspects='traditional')[source]
Check if the Moon is void of course.
The Moon is void of course (VOC) when it will not complete any major Ptolemaic aspect (conjunction, sextile, square, trine, opposition) before leaving its current sign. This is traditionally considered an inauspicious time for beginning new ventures.
- Parameters:
aspects (
Literal['traditional','modern']) – Planet set to consider: - “traditional”: Sun through Saturn (visible planets) - “modern”: Includes Uranus, Neptune, Pluto- Return type:
VOCMoonResult- Returns:
VOCMoonResult with void status and timing details
Example:
voc = chart.voc_moon() if voc.is_void: print(f"Moon is VOC until {voc.void_until}") print(f"Will enter {voc.next_sign}") else: print(f"Moon will {voc.next_aspect}") print(f"Aspect perfects at {voc.void_until}")
- zodiac_type: Any = None
- zodiacal_releasing(lot='Part of Fortune')[source]
Get the zodiacal releasing full life timeline for a given lot.
- Parameters:
lot (
str) – Name of timeline’s lot (defaults to ‘Part of Fortune’)- Return type:
ZRTimeline- Returns:
Zodiacal releasing full timeline object
- zr_at_age(age, lot='Part of Fortune')[source]
Get the zodiacal releasing periods for a given age.
- class stellium.visualization.layers.base.CelestialPosition(name, object_type, longitude, latitude=0.0, distance=0.0, speed_longitude=0.0, speed_latitude=0.0, speed_distance=0.0, declination=None, right_ascension=None, phase=None)[source]
Bases:
objectImmutable representation of a celestial object’s position.
This is the OUTPUT of ephemeris calculations.
- property declination_direction: str
‘north’, ‘south’, or ‘none’.
- Type:
Direction of declination
- distance: float = 0.0
- property is_out_of_bounds: bool
Planet is beyond the Sun’s maximum declination (~23°27’).
Out-of-bounds planets are considered to have extra intensity, unpredictability, or unconventional expression in their significations.
The Sun’s declination varies between approximately +23.4367° and -23.4367° (the Tropic of Cancer and Tropic of Capricorn). When a planet exceeds these bounds, it’s “out of bounds.”
Moon, Mercury, Mars, and Venus can go out of bounds. Jupiter, Saturn, and outer planets rarely or never do.
- is_retrograde: bool
- latitude: float = 0.0
- longitude: float
- name: str
- object_type: ObjectType
- sign: str
- sign_degree: float
- property sign_position: str
Human-readable sign position (e.g. 15°23’ Aries)
- speed_distance: float = 0.0
- speed_latitude: float = 0.0
- speed_longitude: float = 0.0
- class stellium.visualization.layers.base.ChartRenderer(size=600, rotation=0.0, theme=None, style_config=None, zodiac_palette=None, aspect_palette=None, planet_glyph_palette=None, color_sign_info=False)[source]
Bases:
objectThe core chart drawing canvas and coordinate system.
This class holds the SVG drawing object and provides the geometric utilities for layers to draw themselves. It acts as the “Context” in the strategy pattern.
- astrological_to_svg_angle(astro_deg)[source]
Converts astrological degrees (0° = Aries) to SVG degrees (0° = 3 o’clock), appling the chart’s rotation.
Our system: 0° Aries is at 9 o’clock (180° SVG). Rotation is COUNTER-CLOCKWISE.
- Return type:
- class stellium.visualization.layers.base.HouseCusps(system, cusps)[source]
Bases:
objectImmutable house cusp data.
- get_description(house_number)[source]
Get human-readable cusp description for a specific house.
- Return type:
- system: str
- class stellium.visualization.layers.base.PlanetGlyphPalette(value)[source]
Bases:
StrEnumAvailable color palettes for planet glyphs.
- CHAKRA = 'chakra'
- DEFAULT = 'default'
- ELEMENT = 'element'
- INFERNO = 'inferno'
- LUMINARIES = 'luminaries'
- PLANET_TYPE = 'planet_type'
- PLASMA = 'plasma'
- RAINBOW = 'rainbow'
- SIGN_RULER = 'sign_ruler'
- TURBO = 'turbo'
- VIRIDIS = 'viridis'
- class stellium.visualization.layers.base.UnknownTimeChart(datetime, location, positions, house_systems=<factory>, house_placements=<factory>, aspects=(), declination_aspects=(), metadata=<factory>, zodiac_type=None, ayanamsa=None, ayanamsa_value=None, calculation_timestamp=<factory>, chart_tags=(), moon_range=None)[source]
Bases:
CalculatedChartA natal chart calculated without known birth time.
Inherits from CalculatedChart for compatibility with all existing code, but has some key differences: - Houses are empty (no house cusps without birth time) - Angles are not calculated (no Asc/MC/Dsc/IC) - Moon is shown as a range rather than single position - Planetary positions are calculated for noon
The chart can still be: - Visualized (without houses, with moon arc) - Exported to JSON - Used for aspect calculations (using noon Moon) - Used for dignity calculations (planets only)
- moon_range
MoonRange showing the Moon’s possible positions throughout the day
- get_angles()[source]
Angles are not available for unknown time charts.
- Return type:
- get_house(object_name, system_name=None)[source]
Houses are not available for unknown time charts.
- Return type:
- get_houses(system_name=None)[source]
Houses are not available for unknown time charts.
- Return type:
- property is_time_unknown: bool
Always True for this chart type.
- moon_range: MoonRange = None
- class stellium.visualization.layers.base.ZodiacPalette(value)[source]
Bases:
StrEnumAvailable color palettes for the zodiac wheel.
- CARDINALITY = 'cardinality'
- CIVIDIS = 'cividis'
- COOLWARM = 'coolwarm'
- ELEMENTAL = 'elemental'
- ELEMENTAL_DARK = 'elemental_dark'
- ELEMENTAL_MIDNIGHT = 'elemental_midnight'
- ELEMENTAL_NEON = 'elemental_neon'
- ELEMENTAL_SEPIA = 'elemental_sepia'
- GREY = 'grey'
- INFERNO = 'inferno'
- MAGMA = 'magma'
- PLASMA = 'plasma'
- RAINBOW = 'rainbow'
- RAINBOW_CELESTIAL = 'rainbow_celestial'
- RAINBOW_DARK = 'rainbow_dark'
- RAINBOW_MIDNIGHT = 'rainbow_midnight'
- RAINBOW_NEON = 'rainbow_neon'
- RAINBOW_SEPIA = 'rainbow_sepia'
- SPECTRAL = 'spectral'
- TURBO = 'turbo'
- VIRIDIS = 'viridis'
- stellium.visualization.layers.base.adjust_color_for_contrast(original_color, background_color, min_contrast=4.5, max_iterations=20)[source]
Adjust a color to ensure minimum contrast against a background.
This algorithm: 1. Checks if original color already has sufficient contrast 2. If not, determines if background is light or dark 3. Adjusts the color’s lightness/darkness in the opposite direction 4. Iterates until minimum contrast is achieved
- Parameters:
- Return type:
- Returns:
Adjusted hex color that meets minimum contrast
- stellium.visualization.layers.base.embed_svg_glyph(dwg, svg_content, x, y, size, fill_color=None)[source]
Embed an SVG glyph inline as a nested SVG element.
This function parses SVG content and embeds it directly into the drawing as a nested <svg> element with proper positioning and scaling. This approach works across all browsers and SVG viewers, unlike external image references.
- Parameters:
dwg (
Drawing) – The svgwrite Drawing to add the element tosvg_content (
str) – The raw SVG content string (from get_glyph())x (
float) – Center x coordinate for the glyphy (
float) – Center y coordinate for the glyphsize (
float) – Desired size (width and height) in pixelsfill_color (
str|None) – Optional color to override stroke/fill (for theming)
- Return type:
- stellium.visualization.layers.base.get_aspect_palette_colors(palette)[source]
Get aspect colors for a given palette.
Returns a dictionary mapping aspect names to hex colors. Includes Conjunction, Sextile, Square, Trine, Opposition, and minor aspects (Semisextile, Semisquare, Sesquisquare, Quincunx). Results are cached in memory for performance.
- stellium.visualization.layers.base.get_glyph(object_name)[source]
Get the glyph for a celestial object, with registry lookup and fallback.
- Parameters:
object_name (
str) – Name of the object (e.g., “Sun”, “Mean Apogee”, “ASC”)- Returns:
“type”: “unicode” or “svg”
”value”: glyph string (unicode) or SVG content string (for inline embedding)
- Return type:
Dictionary with
- stellium.visualization.layers.base.get_palette_colors(palette)[source]
Get the color list for a zodiac wheel palette.
Returns a list of 12 colors (one per sign, starting with Aries). Results are cached in memory for performance.
Special case: If palette is a string starting with “single_color:”, extracts the hex color and returns 12 copies of it for a monochrome wheel.
- stellium.visualization.layers.base.get_planet_glyph_color(planet_name, palette, theme_default_color='#222222')[source]
Get the color for a planet glyph based on palette.
- stellium.visualization.layers.base.get_sign_info_color(sign_index, zodiac_palette, background_color, min_contrast=4.5)[source]
Get an adaptive color for sign glyph in planet info stack.
This function: 1. Gets the sign’s zodiac wheel color from the palette 2. Adjusts it for contrast against the background 3. Returns a color that’s readable while maintaining zodiac color story
- Parameters:
- Return type:
- Returns:
Hex color for the sign glyph that contrasts with background
Zodiac ring layer - renders the zodiac wheel with signs and degrees.
- class stellium.visualization.layers.zodiac.ZodiacLayer(palette=ZodiacPalette.GREY, style_override=None, show_degree_ticks=False)[source]
Bases:
objectRenders the outer Zodiac ring, including glyphs and tick marks.
House cusp layers - inner and outer house cusp rendering.
- class stellium.visualization.layers.houses.HouseCuspLayer(house_system_name, style_override=None, wheel_index=0, chart=None)[source]
Bases:
objectRenders a single set of house cusps and numbers.
To draw multiple systems, add multiple layers.
For multiwheel charts, use wheel_index to specify which chart ring to render: - wheel_index=0: Chart 1 (innermost) - wheel_index=1: Chart 2 - wheel_index=2: Chart 3 - wheel_index=3: Chart 4 (outermost, just inside zodiac)
The layer will look up radii from the renderer using keys like: - chart{N}_ring_outer, chart{N}_ring_inner (ring bounds) - chart{N}_house_number (number placement)
And fill colors from theme: - chart{N}_fill_1, chart{N}_fill_2 (alternating fills)
- class stellium.visualization.layers.houses.OuterHouseCuspLayer(house_system_name, style_override=None)[source]
Bases:
objectRenders house cusps for the OUTER wheel (chart2 in comparisons).
This draws house cusp lines and numbers outside the zodiac ring, with a distinct visual style from the inner chart’s houses.
Deprecated since version Use: HouseCuspLayer(wheel_index=1) instead. This class renders outside the zodiac ring (legacy biwheel style), while the new multiwheel system renders all charts inside the zodiac ring.
Planet layers - planet glyphs, positions, and moon range.
- class stellium.visualization.layers.planets.MoonRangeLayer(arc_color=None, arc_opacity=0.4)[source]
Bases:
objectRenders a shaded arc showing the Moon’s possible position range.
Used for unknown birth time charts where the Moon could be anywhere within a ~12-14° range throughout the day.
The arc is drawn as a semi-transparent wedge from the day-start position to the day-end position, with the Moon glyph at the noon position.
- class stellium.visualization.layers.planets.PlanetLayer(planet_set, radius_key='planet_ring', style_override=None, use_outer_wheel_color=False, info_stack_direction='inward', show_info_stack=True, show_position_ticks=False, wheel_index=0, info_mode='full', info_stack_distance=0.8, glyph_size_override=None)[source]
Bases:
objectRenders a set of planets at a specific radius.
For multiwheel charts, use wheel_index to specify which chart ring to render: - wheel_index=0: Chart 1 (innermost) - wheel_index=1: Chart 2 - wheel_index=2: Chart 3 - wheel_index=3: Chart 4 (outermost, just inside zodiac)
The info_mode parameter controls how much detail to show: - “full”: Degree + sign glyph + minutes (default for single charts) - “compact”: Degree only, e.g. “15°” (good for multiwheel) - “no_sign”: Degree + minutes, no sign glyph, e.g. “15°32’” - “none”: No info stack, glyph only
Aspect layers - aspect lines, patterns, and multiwheel aspects.
- class stellium.visualization.layers.aspects.AspectLayer(style_override=None)[source]
Bases:
objectRenders the aspect lines within the chart.
- class stellium.visualization.layers.aspects.ChartShapeLayer(position='bottom-right', style_override=None)[source]
Bases:
objectRenders chart shape information in a corner.
Displays the overall pattern/distribution of planets (Bundle, Bowl, Bucket, etc.).
- DEFAULT_STYLE = {'font_weight': 'normal', 'line_height': 14, 'text_color': '#333333', 'text_size': '11px', 'title_weight': 'bold'}
- class stellium.visualization.layers.aspects.MultiWheelAspectLayer(style_override=None)[source]
Bases:
objectRenders cross-chart aspect lines for MultiWheel charts.
Only used for 2-chart multiwheels (biwheels), where showing aspects between the two charts is useful and not too cluttered. For 3-4 chart multiwheels, aspect lines are omitted due to visual complexity.
Angle layers - ASC, MC, DSC, IC angle rendering.
- class stellium.visualization.layers.angles.AngleLayer(style_override=None, wheel_index=0, chart=None)[source]
Bases:
objectRenders the primary chart angles (ASC, MC, DSC, IC).
For multiwheel charts, use wheel_index to specify which chart’s angles to render. Typically only wheel_index=0 (innermost chart) has meaningful angles since transit/progressed charts use the natal houses.
- class stellium.visualization.layers.angles.OuterAngleLayer(style_override=None)[source]
Bases:
objectRenders the outer wheel angles (for comparison charts).
Deprecated since version Use: AngleLayer(wheel_index=1) instead. This class renders outside the zodiac ring (legacy biwheel style), while the new multiwheel system renders all charts inside the zodiac ring.
Chart frame layers - header, borders, and ring boundaries.
- class stellium.visualization.layers.chart_frame.HeaderLayer(height=70, name_font_size='18px', name_font_family="Baskerville, 'Libre Baskerville', Georgia, serif", name_font_weight='600', name_font_style='italic', details_font_size='12px', line_height=16, coord_precision=4)[source]
Bases:
objectRenders the chart header band at the top of the canvas.
Displays native information prominently: - Single chart: Name, location, datetime, timezone, coordinates - Biwheel: Two-column layout with chart1 info left-aligned, chart2 right-aligned - Synthesis: “Composite: Name1 & Name2” or “Davison: Name1 & Name2” with midpoint info
The header uses Baskerville italic-semibold for names (elegant, classical feel) and the normal text font for details.
- class stellium.visualization.layers.chart_frame.OuterBorderLayer[source]
Bases:
objectRenders the outer containment border for comparison/biwheel charts.
- class stellium.visualization.layers.chart_frame.RingBoundaryLayer(chart_count=2, style_override=None)[source]
Bases:
objectRenders circular boundary lines between chart rings in a multiwheel chart.
Draws circles at the boundaries between: - Each chart ring (chart1_ring_outer, chart2_ring_outer, etc.) - The outermost chart and the zodiac ring (zodiac_ring_inner)
Uses the theme’s ring_border styling for color and width.
Info corner layers - chart info, aspect counts, element/modality tables.
- class stellium.visualization.layers.info_corners.AspectCountsLayer(position='top-right', style_override=None)[source]
Bases:
objectRenders aspect counts summary in a corner of the chart.
Displays count of each aspect type with glyphs.
- DEFAULT_STYLE = {'font_weight': 'normal', 'line_height': 14, 'text_color': '#333333', 'text_size': '11px', 'title_weight': 'bold'}
- class stellium.visualization.layers.info_corners.ChartInfoLayer(position='top-left', fields=None, style_override=None, house_systems=None, header_enabled=False)[source]
Bases:
objectRenders chart metadata information in a corner of the chart.
When header is disabled: displays all native info (name, location, datetime, etc.) When header is enabled: displays only calculation settings (house system, ephemeris)
- CALCULATION_FIELDS = {'ephemeris', 'house_system'}
- DEFAULT_STYLE = {'font_weight': 'normal', 'line_height': 14, 'name_size': '16px', 'name_weight': 'bold', 'text_color': '#333333', 'text_size': '11px'}
- NATIVE_INFO_FIELDS = {'coordinates', 'datetime', 'location', 'name', 'timezone'}
- class stellium.visualization.layers.info_corners.ElementModalityTableLayer(position='bottom-left', style_override=None)[source]
Bases:
objectRenders element × modality cross-table in a corner.
Shows distribution of planets across elements (Fire, Earth, Air, Water) and modalities (Cardinal, Fixed, Mutable).
- DEFAULT_STYLE = {'col_width': 28, 'font_weight': 'normal', 'line_height': 13, 'text_color': '#333333', 'text_size': '10px', 'title_weight': 'bold'}
- ELEMENT_SYMBOLS = {'Air': '🜁', 'Earth': '🜃', 'Fire': '🜂', 'Water': '🜄'}
Extended canvas layers for position tables and aspectarian grids.
These layers render tabular data outside the main chart wheel, requiring an extended canvas with additional space.
- class stellium.visualization.extended_canvas.AspectarianLayer(x_offset=0, y_offset=0, style_override=None, object_types=None, config=None, detailed=False)[source]
Bases:
objectRenders an aspectarian grid (triangle aspect table).
Shows aspects between all planets in a classic triangle grid format. Respects chart theme colors.
Supports two modes: - Simple (default): Large aspect glyphs only - Detailed: Smaller glyphs with orb value and A/S (applying/separating) indicator
- DEFAULT_STYLE = {'cell_size': 24, 'font_weight': 'normal', 'grid_color': '#CCCCCC', 'header_color': '#222222', 'header_size': '10px', 'header_weight': 'bold', 'orb_size': '6px', 'show_grid': True, 'text_color': '#333333', 'text_size': '14px', 'text_size_detailed': '11px'}
- class stellium.visualization.extended_canvas.HouseCuspTableLayer(x_offset=0, y_offset=0, style_override=None, config=None)[source]
Bases:
objectRenders a table of house cusps with sign placements.
Shows house number, cusp longitude, sign, and degree in sign. Respects chart theme colors.
- DEFAULT_STYLE = {'col_spacing': 55, 'font_weight': 'normal', 'header_color': '#222222', 'header_size': '11px', 'header_weight': 'bold', 'line_height': 16, 'text_color': '#333333', 'text_size': '10px'}
- class stellium.visualization.extended_canvas.PositionTableLayer(x_offset=0, y_offset=0, style_override=None, object_types=None, config=None)[source]
Bases:
objectRenders a table of planetary positions.
Shows planet name, sign, degree, house, and speed in a tabular format. Respects chart theme colors.
- DEFAULT_STYLE = {'col_spacing': 55, 'font_weight': 'normal', 'header_color': '#222222', 'header_size': '11px', 'header_weight': 'bold', 'line_height': 16, 'show_house': True, 'show_speed': True, 'text_color': '#333333', 'text_size': '10px'}
- stellium.visualization.extended_canvas.generate_aspectarian_svg(chart, output_path=None, cell_size=None, detailed=False, theme=None, aspect_palette=None, padding=None)[source]
Generate a standalone aspectarian SVG (triangle for single charts, square for comparisons).
- Parameters:
chart – CalculatedChart or Comparison object
output_path (
str|None) – Optional path to save SVG file. If None, returns SVG string.cell_size (
int|None) – Size of each grid cell in pixels (default: from config, typically 24)detailed (
bool) – If True, show orb and applying/separating indicator (default: False)theme (
str|None) – Optional theme name (e.g., “dark”, “midnight”)padding (
int|None) – Padding around the grid in pixels (default: from config, typically 10)
- Return type:
- Returns:
SVG string if output_path is None, otherwise the output_path
Example
# Generate and save triangle aspectarian chart = ChartBuilder.from_notable(“Albert Einstein”).with_aspects().calculate() generate_aspectarian_svg(chart, “einstein_aspects.svg”)
# Generate square aspectarian for synastry comparison = ComparisonBuilder.synastry(chart1, chart2).calculate() svg_string = generate_aspectarian_svg(comparison)
# With styling generate_aspectarian_svg(
chart, “aspects.svg”, cell_size=28, detailed=True, theme=”midnight”,
)
- stellium.visualization.extended_canvas.get_aspectarian_dimensions(chart, cell_size=None, padding=None)[source]
Calculate the dimensions of an aspectarian SVG without rendering.
Uses the ContentMeasurer for consistency with the rest of the visualization system.
- Parameters:
- Return type:
- Returns:
Tuple of (width, height) in pixels
Moon phase visualization layer.
Renders accurate moon phase symbols showing the illuminated portion with curved terminator lines.
- class stellium.visualization.moon_phase.MoonPhaseLayer(position=None, show_label=True, style_override=None)[source]
Bases:
objectRenders the moon phase on the chart.
This layer draws an accurate representation of the moon’s current phase using curved terminator lines to show the illuminated portion.
The moon can be positioned in the center or in any corner, and can optionally display the phase name as a text label.
- DEFAULT_STYLE = {'border_color': '#2C3E50', 'border_width': 2, 'label_color': '#2C3E50', 'label_offset': 10, 'label_size': '11px', 'lit_color': '#F8F9FA', 'opacity': 0.95, 'shadow_color': '#2C3E50', 'size': 40}
- render(renderer, dwg, chart)[source]
Render the moon phase.
- Parameters:
renderer (
ChartRenderer) – ChartRenderer instancedwg (
Drawing) – SVG drawing objectchart (
CalculatedChart) – Calculated chart (or MultiWheel - uses innermost chart)
- Return type:
- stellium.visualization.moon_phase.draw_moon_phase_standalone(phase_frac, phase_angle, filename='moon_phase.svg', size=200, style=None)[source]
Draw a standalone moon phase SVG.
Useful for testing or standalone moon phase displays.
- Parameters:
- Return type:
- Returns:
Filename of saved SVG
Example
# Draw a waxing crescent draw_moon_phase_standalone(0.25, 90, “waxing_crescent.svg”)
# Draw a full moon draw_moon_phase_standalone(1.0, 180, “full_moon.svg”)
SVG Grid Layout (stellium.visualization.grid)
Creates subplot-like grid arrangements of multiple charts in a single SVG file. Similar to matplotlib’s subplot functionality but for astrology charts.
- stellium.visualization.grid.draw_chart_grid(charts, filename='chart_grid.svg', labels=None, rows=None, cols=None, chart_size=400, padding=30, themes=None, zodiac_palettes=None, aspect_palettes=None, planet_glyph_palettes=None, color_sign_info=False, color_zodiac_glyphs=False, moon_phase=True, background_color='#FAFAFA')[source]
Draw multiple charts in a grid layout in a single SVG file.
This function creates a subplot-like arrangement of multiple charts, perfect for comparing different chart styles, themes, or palettes.
- Parameters:
charts (
list[CalculatedChart]) – List of CalculatedChart objects to renderfilename (
str) – Output SVG filenamelabels (
list[str] |None) – Optional labels for each chart (displayed at bottom)rows (
int|None) – Number of rows (auto-calculated if None)cols (
int|None) – Number of columns (auto-calculated if None)chart_size (
int) – Size of each individual chart in pixelspadding (
int) – Padding between charts in pixelsthemes (
list[ChartTheme|str] |None) – Optional list of themes (one per chart, or None for default)zodiac_palettes (
list[ZodiacPalette|str] |None) – Optional list of zodiac palettes (one per chart)aspect_palettes (
list[str] |None) – Optional list of aspect palettes (one per chart)planet_glyph_palettes (
list[str] |None) – Optional list of planet glyph palettes (one per chart)color_sign_info (
bool) – Apply adaptive sign info coloring to all chartscolor_zodiac_glyphs (
bool) – Apply adaptive zodiac glyph coloring to all chartsmoon_phase (
bool) – Show moon phase in all chartsbackground_color (
str) – Background color for the entire grid
- Return type:
- Returns:
The filename of the saved chart grid
Example
>>> from stellium import ChartBuilder >>> chart = ChartBuilder.from_notable("Albert Einstein").calculate() >>> # Create grid showing same chart with different themes >>> draw_chart_grid( ... charts=[chart] * 4, ... labels=["Classic", "Dark", "Midnight", "Neon"], ... themes=["classic", "dark", "midnight", "neon"], ... rows=2, ... cols=2, ... )
- stellium.visualization.grid.draw_palette_comparison(chart, filename='palette_comparison.svg', palettes=None, theme=ChartTheme.DARK, chart_size=300, color_zodiac_glyphs=True)[source]
Create a grid comparing the same chart with different zodiac palettes.
- Parameters:
chart (
CalculatedChart) – The chart to render with different palettesfilename (
str) – Output SVG filenamepalettes (
list[ZodiacPalette|str] |None) – List of zodiac palettes to compare (defaults to popular ones)theme (
ChartTheme|str) – Base theme to use (default: dark, works well with colorful palettes)chart_size (
int) – Size of each chart in pixelscolor_zodiac_glyphs (
bool) – Enable adaptive zodiac glyph coloring
- Return type:
- Returns:
The filename of the saved comparison grid
Example
>>> from stellium import ChartBuilder >>> chart = ChartBuilder.from_notable("Albert Einstein").calculate() >>> draw_palette_comparison(chart, "einstein_palettes.svg")
- stellium.visualization.grid.draw_theme_comparison(chart, filename='theme_comparison.svg', themes=None, chart_size=300)[source]
Create a grid comparing the same chart rendered in different themes.
- Parameters:
- Return type:
- Returns:
The filename of the saved comparison grid
Example
>>> from stellium import ChartBuilder >>> chart = ChartBuilder.from_notable("Albert Einstein").calculate() >>> draw_theme_comparison(chart, "einstein_themes.svg")
HTML Reference Sheet Generator (stellium.visualization.reference_sheet)
Generates comprehensive HTML reference sheets showing all available themes, palettes, and their colors for easy reference.
- stellium.visualization.reference_sheet.generate_aspect_palette_reference(filename='aspect_palettes.html')[source]
Generate a reference sheet showing only aspect palettes.
- Return type:
- stellium.visualization.reference_sheet.generate_html_reference(filename='stellium_color_reference.html', include_zodiac=True, include_aspects=True, include_planet_glyphs=True, include_themes=True)[source]
Generate a comprehensive HTML reference sheet for all Stellium colors and palettes.
- Parameters:
- Return type:
- Returns:
The filename of the generated HTML file
Example
>>> from stellium.visualization import generate_html_reference >>> generate_html_reference("colors.html")
- stellium.visualization.reference_sheet.generate_theme_reference(filename='themes.html')[source]
Generate a reference sheet showing only themes.
- Return type:
- stellium.visualization.reference_sheet.generate_zodiac_palette_reference(filename='zodiac_palettes.html')[source]
Generate a reference sheet showing only zodiac palettes.
- Return type:
Graphic Ephemeris Visualization (stellium.visualization.ephemeris)
Renders a graphic ephemeris showing planetary positions over time. The X-axis represents time, the Y-axis represents zodiacal position (optionally compressed to 90° or 45° harmonic).
Example
>>> from stellium.visualization import GraphicEphemeris
>>> eph = GraphicEphemeris(
... start_date="2025-01-01",
... end_date="2025-12-31",
... harmonic=90,
... )
>>> eph.draw("ephemeris_2025.svg")
- class stellium.visualization.ephemeris.AspectCrossing(date, harmonic_position, planet1, planet2, aspect_type, longitude1, longitude2, is_transit_to_natal=False)[source]
Bases:
objectA point where two planet lines cross (aspect in harmonic view).
- aspect_type: str
- date: date
- harmonic_position: float
- is_transit_to_natal: bool = False
- longitude1: float
- longitude2: float
- planet1: str
- planet2: str
- class stellium.visualization.ephemeris.EphemerisDataPoint(date, julian_day, longitude, speed, harmonic_position)[source]
Bases:
objectA single data point for one planet at one time.
- date: date
- harmonic_position: float
- julian_day: float
- longitude: float
- speed: float
- class stellium.visualization.ephemeris.GraphicEphemeris(start_date, end_date, harmonic=90, planets=None, natal_chart=None, natal_planets=None, width=1400, height=900, show_stations=True, show_grid=True, show_legend=True, show_aspects=True, title=None)[source]
Bases:
objectGraphic Ephemeris visualization.
Renders planetary positions over time as a graph, with optional harmonic compression (90° or 45°) to show hard aspects as conjunctions.
Example
>>> eph = GraphicEphemeris( ... start_date="2025-01-01", ... end_date="2025-12-31", ... harmonic=90, ... ) >>> eph.draw("ephemeris_2025.svg")
# Include Chiron and North Node >>> eph = GraphicEphemeris( … start_date=”2025-01-01”, … end_date=”2025-12-31”, … planets=EXTENDED_PLANETS, … )
# With natal chart overlay (shows transit-to-natal aspects) >>> from stellium import ChartBuilder >>> natal = ChartBuilder.from_native(my_native).calculate() >>> eph = GraphicEphemeris( … start_date=”2025-01-01”, … end_date=”2025-12-31”, … natal_chart=natal, … ) >>> eph.draw(“transits_2025.svg”)
- draw(filename='ephemeris.svg')[source]
Render the graphic ephemeris to SVG.
- property plot_height: int
Height of the plot area (excluding margins).
- property plot_width: int
Width of the plot area (excluding margins).
- class stellium.visualization.ephemeris.GraphicEphemerisConfig(start_date, end_date, harmonic=90, planets=<factory>, width=1400, height=900, margin_left=90, margin_right=80, margin_top=60, margin_bottom=80, background_color='#FFFFFF', grid_color='#E8E8E8', grid_color_major='#D0D0D0', axis_color='#666666', text_color='#333333', line_width=2.5, show_stations=True, show_grid=True, show_title=True, show_legend=True, show_aspects=True, title=None, days_per_point=1)[source]
Bases:
objectConfiguration for graphic ephemeris rendering.
- axis_color: str = '#666666'
- background_color: str = '#FFFFFF'
- days_per_point: int = 1
- end_date: date
- grid_color: str = '#E8E8E8'
- grid_color_major: str = '#D0D0D0'
- harmonic: Literal[360, 90, 45] = 90
- height: int = 900
- line_width: float = 2.5
- margin_bottom: int = 80
- margin_left: int = 90
- margin_right: int = 80
- margin_top: int = 60
- show_aspects: bool = True
- show_grid: bool = True
- show_legend: bool = True
- show_stations: bool = True
- show_title: bool = True
- start_date: date
- text_color: str = '#333333'
- width: int = 1400
- class stellium.visualization.ephemeris.NatalPosition(planet, longitude, harmonic_position)[source]
Bases:
objectA natal planet position for overlay on the ephemeris.
- harmonic_position: float
- longitude: float
- planet: str
- class stellium.visualization.ephemeris.StationPoint(date, julian_day, longitude, harmonic_position, station_type)[source]
Bases:
objectA retrograde or direct station point.
- date: date
- harmonic_position: float
- julian_day: float
- longitude: float
- station_type: Literal['retrograde', 'direct']
Atlas PDF Generation (stellium.visualization.atlas)¶
Generate multi-page PDF chart atlases.
AtlasBuilder - Fluent API for creating chart atlas PDFs.
Generates multi-page PDFs with one chart per page, like an old-school astrologer’s chart atlas.
- class stellium.visualization.atlas.builder.AtlasBuilder[source]
Bases:
objectFluent builder for chart atlas PDF generation.
Generates a PDF document with multiple charts, one per page. Supports both natal wheel charts and Uranian dial charts.
Example:
# Basic atlas from list of natives AtlasBuilder().add_natives([native1, native2]).save("atlas.pdf") # With configuration (AtlasBuilder() .add_notable("Albert Einstein") .add_notable("Marie Curie") .with_chart_type("dial", degrees=90) .with_header() .with_theme("midnight") .save("scientists.pdf")) # Mixed chart types (AtlasBuilder() .add_entry(native1, chart_type="wheel") .add_entry(native2, chart_type="dial", degrees=90) .save("mixed.pdf")) # Entire notables database AtlasBuilder.from_all_notables().save("complete_atlas.pdf")
- add_entry(native, chart_type=None, **chart_options)[source]
Add an entry with custom chart configuration.
Allows per-entry chart type and options, overriding defaults.
- Parameters:
- Return type:
AtlasBuilder- Returns:
Self for chaining
Example:
builder.add_entry(native1, chart_type="wheel") builder.add_entry(native2, chart_type="dial", degrees=90)
- add_native(native)[source]
Add a single native to the atlas.
Uses the default chart type and options.
- Parameters:
native (
Native) – Native object with birth data- Return type:
AtlasBuilder- Returns:
Self for chaining
- add_natives(natives)[source]
Add multiple natives to the atlas.
Uses the default chart type and options for all.
- add_notable(name)[source]
Add a notable person by name lookup.
Looks up the notable in the registry and adds their chart.
- Parameters:
name (
str) – Name of the notable (e.g., “Albert Einstein”)- Return type:
AtlasBuilder- Returns:
Self for chaining
- Raises:
ValueError – If notable not found in registry
- add_notables(names)[source]
Add multiple notables by name lookup.
- classmethod from_all_notables(category=None, sort_by='name')[source]
Create an atlas from all notables in the registry.
- Parameters:
- Return type:
AtlasBuilder- Returns:
AtlasBuilder pre-populated with all matching notables
Example:
# All notables AtlasBuilder.from_all_notables().save("complete_atlas.pdf") # Only scientists, sorted by birth date (AtlasBuilder.from_all_notables(category="scientist", sort_by="date") .with_title_page("Famous Scientists") .save("scientists.pdf"))
- render()[source]
Generate the atlas PDF and return as bytes.
Useful for serving directly or further processing.
- Return type:
- Returns:
PDF content as bytes
- Raises:
ValueError – If no entries have been added
ImportError – If typst library is not available
- save(filename=None)[source]
Generate the atlas PDF and save to file.
- Parameters:
filename (
str|None) – Output filename (overrides with_filename if provided)- Return type:
- Returns:
Path to the saved PDF file
- Raises:
ValueError – If no entries have been added
ImportError – If typst library is not available
- with_aspect_counts(enabled=True)[source]
Enable or disable aspect counts corner display.
Shows a summary of aspect counts (conjunctions, trines, etc.) in the top-right corner of each chart.
- Parameters:
enabled (
bool) – True to show aspect counts (default), False to hide- Return type:
AtlasBuilder- Returns:
Self for chaining
- with_aspects(enabled=True)[source]
Enable or disable aspect lines on charts.
- Parameters:
enabled (
bool) – True to show aspects (default), False to hide- Return type:
AtlasBuilder- Returns:
Self for chaining
- with_chart_type(chart_type, **options)[source]
Set the default chart type for all entries.
- Parameters:
- Return type:
AtlasBuilder- Returns:
Self for chaining
Example:
builder.with_chart_type("dial", degrees=90)
- with_element_modality(enabled=True)[source]
Enable or disable element/modality table corner display.
Shows a cross-table of elements (Fire, Earth, Air, Water) and modalities (Cardinal, Fixed, Mutable) in the bottom-left corner.
- Parameters:
enabled (
bool) – True to show table (default), False to hide- Return type:
AtlasBuilder- Returns:
Self for chaining
- with_extended_tables(enabled=True)[source]
Enable extended tables (positions, aspects, houses).
When enabled, pages are rendered in landscape orientation to accommodate the additional table columns.
- Parameters:
enabled (
bool) – True to show extended tables- Return type:
AtlasBuilder- Returns:
Self for chaining
- with_filename(filename)[source]
Set the output filename.
- Parameters:
filename (
str) – Output PDF filename- Return type:
AtlasBuilder- Returns:
Self for chaining
- with_header(enabled=True)[source]
Enable or disable chart headers.
When enabled, each chart shows native name and birth info.
- Parameters:
enabled (
bool) – True to show headers, False to hide- Return type:
AtlasBuilder- Returns:
Self for chaining
- with_page_size(size)[source]
Set the page size for the PDF.
- Parameters:
size (
str) – Page size (“letter”, “a4”, “half-letter”)- Return type:
AtlasBuilder- Returns:
Self for chaining
- with_theme(theme)[source]
Set the visual theme for all charts.
- Parameters:
theme (
str) – Theme name (e.g., “classic”, “midnight”, “dark”, “celestial”)- Return type:
AtlasBuilder- Returns:
Self for chaining
- with_title_page(title)[source]
Add a title page to the atlas.
- Parameters:
title (
str) – Title text for the title page- Return type:
AtlasBuilder- Returns:
Self for chaining
Example:
builder.with_title_page("Famous Scientists")
- with_zodiac_palette(palette)[source]
Set the zodiac ring color palette.
- Parameters:
palette (
str) – Palette name (default: “rainbow”)- Return type:
AtlasBuilder- Returns:
Self for chaining
- without_aspect_counts()[source]
Disable aspect counts corner display.
- Return type:
AtlasBuilder- Returns:
Self for chaining
- without_aspects()[source]
Disable aspect lines on charts.
- Return type:
AtlasBuilder- Returns:
Self for chaining
- without_element_modality()[source]
Disable element/modality table corner display.
- Return type:
AtlasBuilder- Returns:
Self for chaining
- without_header()[source]
Disable chart headers.
- Return type:
AtlasBuilder- Returns:
Self for chaining
- without_info_corners()[source]
Disable all info corner displays (aspect counts and element/modality).
- Return type:
AtlasBuilder- Returns:
Self for chaining
Configuration dataclasses for atlas generation.
- class stellium.visualization.atlas.config.AtlasConfig(entries=<factory>, page_size='letter', theme='classic', zodiac_palette='rainbow', show_header=True, show_aspects=True, show_extended_tables=False, show_aspect_counts=True, show_element_modality=True, title=None, filename='atlas.pdf')[source]
Bases:
objectConfiguration for atlas PDF generation.
- entries
List of AtlasEntry objects (charts to include)
- page_size
Paper size (“letter”, “a4”, “half-letter”)
- theme
Visual theme for charts (e.g., “classic”, “midnight”)
- zodiac_palette
Zodiac ring color palette (default: “rainbow”)
- show_header
Whether to show native info header on each chart
- show_aspects
Whether to show aspect lines on charts
- show_extended_tables
Whether to show extended tables (enables landscape)
- show_aspect_counts
Whether to show aspect counts corner
- show_element_modality
Whether to show element/modality table corner
- title
Optional title for title page (None = no title page)
- filename
Output PDF filename
- entries: list[AtlasEntry]
- filename: str = 'atlas.pdf'
- page_size: str = 'letter'
- show_aspect_counts: bool = True
- show_aspects: bool = True
- show_element_modality: bool = True
- show_extended_tables: bool = False
- show_header: bool = True
- theme: str = 'classic'
- zodiac_palette: str = 'rainbow'
- class stellium.visualization.atlas.config.AtlasEntry(native, chart_type='wheel', chart_options=<factory>)[source]
Bases:
objectSingle entry in the atlas.
Each entry represents one chart page in the PDF.
- native
The Native (birth data) for this chart
- chart_type
Type of chart to render (“wheel” or “dial”)
- chart_options
Additional options for the chart type - For “dial”: {“degrees”: 90} (90, 45, or 360)
- chart_type: str = 'wheel'
- native: Native
AtlasRenderer - Generate atlas PDFs using Typst typesetting.
Renders chart SVGs and compiles them into a multi-page PDF.
Dial Charts (stellium.visualization.dial)¶
Uranian dial chart visualization.
Dial Chart Builder (stellium.visualization.dial.builder)
Fluent API for creating dial chart visualizations.
- class stellium.visualization.dial.builder.DialDrawBuilder(chart, filename='dial.svg', dial_degrees=90)[source]
Bases:
objectFluent builder for dial chart visualization.
Provides a convenient API for creating Uranian/Hamburg school dial charts with support for 90°, 45°, and 360° dials.
Example:
# Basic 90° dial chart.draw_dial("dial.svg").save() # With theme chart.draw_dial("dial.svg").with_theme("midnight").save() # With transits on outer ring chart.draw_dial("dial.svg", degrees=90) .with_outer_ring(transit_chart.get_planets(), label="Transits") .save() # 360° dial with pointer chart.draw_dial("dial.svg", degrees=360) .with_pointer(pointing_to="Sun") .save() # Minimal dial without midpoints chart.draw_dial("dial.svg") .without_midpoints() .save()
- save(to_string=False)[source]
Build and save the dial chart.
- with_filename(filename)[source]
Set the output filename.
- Return type:
DialDrawBuilder
- with_header()[source]
Add a header with chart name and birth details.
The header appears at the top of the dial, showing: - Name (from chart metadata or “Natal Chart”) - Birth date/time, location, and coordinates
- Return type:
DialDrawBuilder
- with_midpoints(ring='outer_ring_1', notation='full')[source]
Show midpoints on an outer ring.
Midpoints are enabled by default. Use this to customize.
- with_outer_ring(positions, ring='outer_ring_2', label='', color=None)[source]
Add positions to an outer ring.
Use this to display transit planets, solar arc directions, progressed positions, etc.
- Parameters:
- Return type:
DialDrawBuilder
Example:
# Add transit planets chart.draw_dial("dial.svg") .with_outer_ring(transit_chart.get_planets(), label="Transits") .save()
- with_pointer(pointing_to=0.0)[source]
Add a rotatable pointer (primarily for 360° dials).
- Parameters:
pointing_to (
float|str) – Where the pointer should point. Can be: - A degree value (0-360 for 360° dial, 0-90 for 90° dial) - A planet name (e.g., “Sun”, “Moon”) - will point to that planet’s position- Return type:
DialDrawBuilder
Example:
# Point to 45° chart.draw_dial("dial.svg", degrees=360).with_pointer(45).save() # Point to natal Sun chart.draw_dial("dial.svg", degrees=360).with_pointer("Sun").save()
- with_rotation(rotation)[source]
Set the dial rotation.
By default, 0° is at the top (12 o’clock). This shifts which degree appears at the top.
- Parameters:
rotation (
float) – Degrees to rotate (positive = clockwise)- Return type:
DialDrawBuilder
- with_size(size)[source]
Set the dial size in pixels.
- Parameters:
size (
int) – Dial diameter in pixels (default: 600)- Return type:
DialDrawBuilder
- with_theme(theme)[source]
Set the visual theme.
Uses the same themes as the main chart visualization: - “classic” (default), “dark”, “midnight”, “neon”, “sepia”, “pastel”, “celestial” - Data science themes: “viridis”, “plasma”, “inferno”, “magma”, “cividis”, “turbo”
- Parameters:
theme (
str|ChartTheme) – Theme name or ChartTheme enum- Return type:
DialDrawBuilder
- with_tnos()[source]
Include Trans-Neptunian Objects on the dial.
TNOs are included by default. This method is provided for clarity when you want to explicitly enable them after disabling.
- Return type:
DialDrawBuilder
- with_uranian()[source]
Include Hamburg/Uranian hypothetical planets on the dial.
Uranian planets are included by default. This method is provided for clarity when you want to explicitly enable them after disabling.
- Return type:
DialDrawBuilder
- without_cardinal_points()[source]
Hide the cardinal point markers.
- Return type:
DialDrawBuilder
- without_graduation()[source]
Hide the graduation tick marks and labels.
- Return type:
DialDrawBuilder
- without_header()[source]
Hide the header.
- Return type:
DialDrawBuilder
- without_midpoints()[source]
Hide midpoints.
- Return type:
DialDrawBuilder
- without_modality_wheel()[source]
Hide the inner modality wheel.
- Return type:
DialDrawBuilder
- without_planets()[source]
Hide the planet glyphs.
- Return type:
DialDrawBuilder
- without_pointer()[source]
Hide the pointer.
- Return type:
DialDrawBuilder
- without_tnos()[source]
Exclude Trans-Neptunian Objects from the dial.
By default, TNOs (Eris, Sedna, Makemake, Haumea, Orcus, Quaoar) are included because they are important in Uranian astrology. Use this to show only traditional planets.
- Return type:
DialDrawBuilder
- without_uranian()[source]
Exclude Hamburg/Uranian hypothetical planets from the dial.
By default, the 8 Uranian planets (Cupido, Hades, Zeus, Kronos, Apollon, Admetos, Vulkanus, Poseidon) are included because they are fundamental to Uranian astrology. Use this to exclude them.
- Return type:
DialDrawBuilder
Dial Chart Renderer (stellium.visualization.dial.renderer)
Core coordinate system and rendering context for dial charts. Similar to ChartRenderer but with longitude compression for 90°/45°/360° dials.
- class stellium.visualization.dial.renderer.DialRenderer(config)[source]
Bases:
objectCore renderer for dial chart visualization.
Provides coordinate transformation for compressed dial charts (90°, 45°, or 360°). The dial compresses the zodiac so that hard aspects appear as conjunctions.
For a 90° dial: - 0° Aries, Cancer, Libra, Capricorn all map to 0° on the dial - 0° Taurus, Leo, Scorpio, Aquarius all map to 30° on the dial - 0° Gemini, Virgo, Sagittarius, Pisces all map to 60° on the dial
The coordinate system places 0° at the top (12 o’clock) and progresses clockwise.
- compress_longitude(longitude)[source]
Compress 360° zodiac longitude to dial degrees.
- Parameters:
longitude (
float) – Zodiac longitude (0-360°)- Return type:
- Returns:
Compressed dial position (0 to dial_degrees)
Examples
For 90° dial: - 0° (Aries) → 0° - 90° (Cancer) → 0° - 180° (Libra) → 0° - 270° (Capricorn) → 0° - 45° (mid-Taurus) → 45° - 135° (mid-Leo) → 45°
- create_drawing()[source]
Create the SVG drawing with background.
- Return type:
Drawing- Returns:
svgwrite.Drawing ready for layers to render into
- dial_to_svg_angle(dial_deg)[source]
Convert dial degrees to SVG angle.
SVG coordinate system: - 0° = 3 o’clock (right) - 90° = 6 o’clock (bottom) - Angles increase clockwise
Dial coordinate system: - 0° = 12 o’clock (top) - Angles increase clockwise - Rotation shifts where 0° appears
- draw_arc(dwg, start_deg, end_deg, radius, **kwargs)[source]
Draw an arc on the dial.
- draw_circle(dwg, radius, **kwargs)[source]
Draw a circle centered on the dial.
- draw_line_radial(dwg, dial_deg, inner_radius, outer_radius, **kwargs)[source]
Draw a radial line from inner to outer radius at a given dial degree.
- get_cardinal_points()[source]
Get the cardinal point positions for this dial size.
For 90° dial: 0°, 22.5°, 45°, 67.5° For 45° dial: 0°, 11.25°, 22.5°, 33.75° For 360° dial: 0°, 90°, 180°, 270°
- get_modality_sectors()[source]
Get the modality sector definitions for this dial.
For 90° dial, each 30° sector represents one modality: - 0°-30°: Cardinal (Aries, Cancer, Libra, Capricorn) - 30°-60°: Fixed (Taurus, Leo, Scorpio, Aquarius) - 60°-90°: Mutable (Gemini, Virgo, Sagittarius, Pisces)
- longitude_to_cartesian(longitude, radius)[source]
Convert zodiac longitude directly to cartesian coordinates.
Convenience method that compresses longitude and converts to cartesian in one step.
- polar_to_cartesian(dial_deg, radius)[source]
Convert dial degree and radius to (x, y) cartesian coordinates.
Configuration dataclasses for dial visualization.
Colors and styling are derived from themes (same as main chart) to ensure visual consistency. The DialConfig only stores structural settings; actual colors come from the theme at render time.
- class stellium.visualization.dial.config.DialCardinalConfig(show_arrows=True, arrow_width=2.0, show_accents=True, accent_width=4.0)[source]
Bases:
objectConfiguration for cardinal point markers (structural settings only).
- accent_width: float = 4.0
- arrow_width: float = 2.0
- show_accents: bool = True
- show_arrows: bool = True
- class stellium.visualization.dial.config.DialConfig(dial_degrees=90, size=600, rotation=0.0, theme=None, filename='dial.svg', radii=<factory>, graduation=<factory>, cardinal=<factory>, modality=<factory>, planet=<factory>, pointer=<factory>, header=<factory>, show_graduation=True, show_cardinal_points=True, show_modality_wheel=True, show_planets=True, show_midpoints=True, show_pointer=False, show_header=False, midpoint_ring='outer_ring_1', midpoint_notation='full', pointer_target=0.0)[source]
Bases:
objectComplete configuration for dial visualization.
This is the main config class that contains all dial settings. Colors are derived from the theme at render time for consistency with the main chart visualization system.
- cardinal: DialCardinalConfig
- dial_degrees: int = 90
- filename: str = 'dial.svg'
- get_dial_style()[source]
Get dial-specific style settings derived from the theme.
Maps theme colors to dial elements for easy access by layers.
- Return type:
DialStyle
- get_style()[source]
Get the complete style dictionary from the theme.
Returns theme-derived colors for all dial elements. Falls back to classic theme if no theme specified.
- graduation: DialGraduationConfig
- header: DialHeaderConfig
- midpoint_notation: str = 'full'
- midpoint_ring: str = 'outer_ring_1'
- modality: DialModalityConfig
- planet: DialPlanetConfig
- pointer: DialPointerConfig
- radii: DialRadii
- rotation: float = 0.0
- show_cardinal_points: bool = True
- show_graduation: bool = True
- show_header: bool = False
- show_midpoints: bool = True
- show_modality_wheel: bool = True
- show_planets: bool = True
- show_pointer: bool = False
- size: int = 600
- class stellium.visualization.dial.config.DialGraduationConfig(tick_1_degree=0.3, tick_5_degree=0.5, tick_10_degree=0.7, show_labels=True, label_interval=5, label_font_size='8px', tick_width=0.5)[source]
Bases:
objectConfiguration for graduation tick marks (structural settings only).
- label_font_size: str = '8px'
- label_interval: int = 5
- show_labels: bool = True
- tick_10_degree: float = 0.7
- tick_1_degree: float = 0.3
- tick_5_degree: float = 0.5
- tick_width: float = 0.5
- class stellium.visualization.dial.config.DialHeaderConfig(height=60, name_font_size='16px', name_font_family="Baskerville, 'Libre Baskerville', Georgia, serif", name_font_weight='600', name_font_style='italic', details_font_size='11px', line_height=14)[source]
Bases:
objectConfiguration for dial header (structural settings only).
- details_font_size: str = '11px'
- height: int = 60
- line_height: int = 14
- name_font_family: str = "Baskerville, 'Libre Baskerville', Georgia, serif"
- name_font_size: str = '16px'
- name_font_style: str = 'italic'
- name_font_weight: str = '600'
- class stellium.visualization.dial.config.DialModalityConfig(sector_line_width=1.0, glyph_font_size='14px')[source]
Bases:
objectConfiguration for inner modality wheel (structural settings only).
- glyph_font_size: str = '14px'
- sector_line_width: float = 1.0
- class stellium.visualization.dial.config.DialPlanetConfig(glyph_font_size='18px', show_ticks=True, tick_width=1.0, tick_length=8.0, connector_dash='2,2', connector_width=0.5, min_glyph_spacing=15.0)[source]
Bases:
objectConfiguration for planet display (structural settings only).
- connector_dash: str = '2,2'
- connector_width: float = 0.5
- glyph_font_size: str = '18px'
- min_glyph_spacing: float = 15.0
- show_ticks: bool = True
- tick_length: float = 8.0
- tick_width: float = 1.0
- class stellium.visualization.dial.config.DialPointerConfig(width=2.0, arrow_size=10.0, show_center_circle=True, center_circle_radius=5.0)[source]
Bases:
objectConfiguration for 360° dial pointer (structural settings only).
- arrow_size: float = 10.0
- center_circle_radius: float = 5.0
- show_center_circle: bool = True
- width: float = 2.0
- class stellium.visualization.dial.config.DialRadii(outer_ring_3=0.48, outer_ring_2=0.44, outer_ring_1=0.4, graduation_outer=0.36, graduation_inner=0.32, planet_ring=0.26, modality_outer=0.2, modality_inner=0.08)[source]
Bases:
objectRadii configuration for dial chart rings.
All values are multipliers of the base size (0.0 to 0.5). Rings are listed from outermost to innermost.
- graduation_inner: float = 0.32
- graduation_outer: float = 0.36
- modality_inner: float = 0.08
- modality_outer: float = 0.2
- outer_ring_1: float = 0.4
- outer_ring_2: float = 0.44
- outer_ring_3: float = 0.48
- planet_ring: float = 0.26
- class stellium.visualization.dial.config.DialStyle(background_color='#FFFFFF', graduation_ring_color='#EEEEEE', graduation_tick_color='#333333', graduation_label_color='#444444', cardinal_arrow_color='#000000', cardinal_accent_color='#000000', modality_sector_color_1='#F5F5F5', modality_sector_color_2='#FFFFFF', modality_line_color='#CCCCCC', modality_glyph_color='#555555', planet_glyph_color='#222222', planet_tick_color='#666666', planet_connector_color='#999999', pointer_color='#000000', font_family_glyphs='"Symbola", "Noto Sans Symbols", serif', font_family_text='"Arial", "Helvetica", sans-serif')[source]
Bases:
objectDial-specific style settings derived from a theme.
This maps the main chart theme colors to dial-specific elements, providing a convenient interface for layers to access colors.
- background_color: str = '#FFFFFF'
- cardinal_accent_color: str = '#000000'
- cardinal_arrow_color: str = '#000000'
- font_family_glyphs: str = '"Symbola", "Noto Sans Symbols", serif'
- font_family_text: str = '"Arial", "Helvetica", sans-serif'
- classmethod from_theme_style(style)[source]
Create DialStyle from a theme style dictionary.
Maps theme colors to dial elements: - Graduation uses zodiac ring colors - Cardinal points use angle colors - Modality uses house fill colors - Planets use planet colors
- Return type:
DialStyle
- graduation_label_color: str = '#444444'
- graduation_ring_color: str = '#EEEEEE'
- graduation_tick_color: str = '#333333'
- modality_glyph_color: str = '#555555'
- modality_line_color: str = '#CCCCCC'
- modality_sector_color_1: str = '#F5F5F5'
- modality_sector_color_2: str = '#FFFFFF'
- planet_connector_color: str = '#999999'
- planet_glyph_color: str = '#222222'
- planet_tick_color: str = '#666666'
- pointer_color: str = '#000000'
Dial Chart Layers (stellium.visualization.dial.layers)
Concrete layer implementations for dial chart visualization. Each layer draws one specific part of the dial chart.
- class stellium.visualization.dial.layers.DialBackgroundLayer[source]
Bases:
objectRenders the dial background and outer border.
- class stellium.visualization.dial.layers.DialCardinalLayer[source]
Bases:
objectRenders the cardinal point markers (arrows and accent marks).
For 90° dial: marks at 0°, 22.5°, 45°, 67.5° These represent the cardinal cross of the zodiac.
- class stellium.visualization.dial.layers.DialGraduationLayer[source]
Bases:
objectRenders the graduated tick marks and degree labels on the outer ring.
Draws: - Small tick marks every 1° - Medium tick marks every 5° - Labels at configured intervals (default every 5°)
- class stellium.visualization.dial.layers.DialHeaderLayer(config=None)[source]
Bases:
objectRenders the chart header at the top of the dial canvas.
Displays: - Name (from chart metadata, or “Natal Chart” as fallback) - Birth date/time, location, and coordinates
- class stellium.visualization.dial.layers.DialMidpointLayer(ring='outer_ring_1', notation='full')[source]
Bases:
objectRenders midpoints on an outer ring of the dial.
Midpoints are the halfway points between two planets. Each midpoint has: - A tick mark at its true position - A label/glyph with collision avoidance - A dashed connector line if displaced
- class stellium.visualization.dial.layers.DialModalityLayer[source]
Bases:
objectRenders the inner modality wheel with zodiac glyphs.
The wheel is divided into 3 sectors (Cardinal, Fixed, Mutable), each containing the 4 zodiac signs of that modality.
- class stellium.visualization.dial.layers.DialOuterRingLayer(positions, ring='outer_ring_2', label='', glyph_color=None)[source]
Bases:
objectGeneric outer ring layer for additional chart data.
Can display transit planets, solar arc directions, progressions, etc. Each outer ring has: - Border circles defining the ring area - Tick marks at true positions - Glyphs with collision detection - Dashed connector lines when displaced
- class stellium.visualization.dial.layers.DialPlanetLayer(ring='planet_ring', include_tnos=True, include_uranian=True)[source]
Bases:
objectRenders planet glyphs on the dial with collision detection.
By default, includes: - All 10 planets (Sun through Pluto) - Trans-Neptunian Objects (Eris, Sedna, Makemake, Haumea, Orcus, Quaoar) - Hamburg/Uranian hypothetical planets (Cupido, Hades, Zeus, Kronos,
Apollon, Admetos, Vulkanus, Poseidon)
Draws: - Tick marks at true (compressed) positions - Planet glyphs with collision avoidance - Dashed connector lines when glyphs are displaced
- class stellium.visualization.dial.layers.DialPointerLayer(pointing_to=0.0)[source]
Bases:
objectRenders the rotatable pointer for 360° dials.
The pointer is a double-ended arrow that can point to any degree.
- class stellium.visualization.dial.layers.IDialLayer(*args, **kwargs)[source]
Bases:
ProtocolProtocol for dial chart layers.
- stellium.visualization.dial.layers.resolve_dial_collisions(positions, dial_degrees, min_spacing_360=8.0)[source]
Resolve collisions between glyphs on a dial chart.
Uses a spreading algorithm that scales appropriately for the dial size. The min_spacing is specified for a 360° chart and automatically scaled.
- Parameters:
- Return type:
- Returns:
Updated positions list with adjusted display_deg values
Layout Engine (stellium.visualization.layout)¶
Planet label collision avoidance and layout measurement.
- class stellium.visualization.layout.engine.BoundingBox(position, dimensions)[source]
Bases:
objectRepresents a positioned element with dimensions.
- property bottom: float
- dimensions: Dimensions
- position: Position
- property right: float
- class stellium.visualization.layout.engine.ChartElement(*args, **kwargs)[source]
Bases:
ProtocolProtocol for measurable chart elements.
- measure()[source]
Calculate dimensions without rendering.
- Return type:
Dimensions
- class stellium.visualization.layout.engine.LayoutEngine(config)[source]
Bases:
objectThe heart of the system - calculates all positions before rendering.
This is a pure calculation engine - no rendering, no side effects.
- calculate_layout(chart)[source]
Calculate complete layout for the chart.
Steps: 1. Measure all enabled elements 2. Calculate table positions and sizes 3. Calculate required canvas size (scaled for multiwheel) 4. Calculate wheel size (possibly grown) 5. Center everything 6. Position info corners (with collision detection) 7. Return complete layout specification
- Return type:
LayoutResult
- class stellium.visualization.layout.engine.LayoutResult(canvas_dimensions, wheel_position, wheel_size, wheel_radii, corners, tables, wheel_grew, actual_margins, header_enabled=False, header_height=0)[source]
Bases:
objectComplete layout specification - everything positioned!
- canvas_dimensions: Dimensions
- header_enabled: bool = False
- header_height: int = 0
- wheel_grew: bool
- wheel_position: Position
- wheel_size: int
- class stellium.visualization.layout.engine.Position(x, y)[source]
Bases:
objectRepresents x, y coordinates.
- x: float
- y: float
- class stellium.visualization.layout.engine.TableLayoutSpec(total_width, total_height, table_positions, table_dimensions)[source]
Bases:
objectSpecification for table layout (before final positioning).
This represents the relative layout of tables to each other, which will be finalized once we know the wheel position. It’s the blueprint for the tables.
- static empty()[source]
Create an empty layout spec when tables are disabled.
- Return type:
TableLayoutSpec
- total_height: float
- total_width: float
Measures elements before rendering.
- class stellium.visualization.layout.measurer.ContentMeasurer[source]
Bases:
objectMeasures chart elements without rendering them.
This is crucial for calculating layout before creating the SVG.
- measure_aspectarian(chart, config)[source]
Measure aspectarian grid dimensions.
Triangle for single charts, square for comparisons/multicharts.
- Return type:
Dimensions
- measure_corner_element(element_name, chart, config)[source]
Estimate dimensions for corner elements.
These are roughly fixed size, but we can be more precise by counting lines of text, etc.
- Return type:
Dimensions
- measure_house_table(chart, config)[source]
Measure house cusp table dimensions.
Always 12 rows (houses). For multiple house systems, adds columns (sign + degree) for each system. For comparison/multichart, shows separate tables for each chart.
- Return type:
Dimensions
- measure_position_table(chart, config)[source]
Measure position table dimensions.
For comparison/multichart with 2 charts, this measures TWO side-by-side tables.
- Return type:
Dimensions
Vedic Charts (stellium.visualization.vedic)¶
North and South Indian chart rendering.
Vedic chart rendering — North Indian (diamond) and South Indian (grid) styles.
- class stellium.visualization.vedic.NorthIndianRenderer(size=500, theme='classic', show_degrees=False, label_style='abbreviation')[source]
Bases:
objectRender a North Indian style Vedic chart as SVG.
Usage:
renderer = NorthIndianRenderer(size=500, theme="classic") svg_string = renderer.render(chart) renderer.render_to_file(chart, "vedic_north.svg")
- class stellium.visualization.vedic.SouthIndianRenderer(size=500, theme='classic', show_degrees=False, show_house_numbers=True, label_style='abbreviation')[source]
Bases:
objectRender a South Indian style Vedic chart as SVG.
Usage:
renderer = SouthIndianRenderer(size=500, theme="classic") svg_string = renderer.render(chart) # Or save directly: renderer.render_to_file(chart, "vedic_south.svg")
North Indian style Vedic chart renderer.
The North Indian chart is a square with an inner diamond (X through it), creating 12 triangular houses. House positions are FIXED:
House 1 = top diamond (ASC — always here) House 4 = left diamond House 7 = bottom diamond House 10 = right diamond Houses 2,3 / 5,6 / 8,9 / 11,12 = side triangles in each quadrant
Signs ROTATE based on the ascendant. The traditional way to indicate which sign is in each house is to write the sign’s ordinal number (1=Aries, 2=Taurus, etc.) at the inner corners of the chart.
- class stellium.visualization.vedic.north_indian.NorthIndianRenderer(size=500, theme='classic', show_degrees=False, label_style='abbreviation')[source]
Bases:
objectRender a North Indian style Vedic chart as SVG.
Usage:
renderer = NorthIndianRenderer(size=500, theme="classic") svg_string = renderer.render(chart) renderer.render_to_file(chart, "vedic_north.svg")
South Indian style Vedic chart renderer.
The South Indian chart is a 4×4 grid where the 4 center cells are merged, creating 12 outer cells. Each cell represents a fixed zodiac sign (Aries is always in the same position). Planets are placed in the cell corresponding to their sign.
- Grid layout (sign indices 0-11, where 0=Aries):
┌──────┬──────┬──────┬──────┐ │ 11 │ 0 │ 1 │ 2 │ │ Pisc │ Arie │ Taur │ Gemi │ ├──────┼──────┴──────┼──────┤ │ 10 │ │ 3 │ │ Aqua │ (center) │ Canc │ ├──────┤ ├──────┤ │ 9 │ │ 4 │ │ Capr │ │ Leo │ ├──────┼──────┬──────┼──────┤ │ 8 │ 7 │ 6 │ 5 │ │ Sagi │ Scor │ Libr │ Virg │ └──────┴──────┴──────┴──────┘
- class stellium.visualization.vedic.south_indian.SouthIndianRenderer(size=500, theme='classic', show_degrees=False, show_house_numbers=True, label_style='abbreviation')[source]
Bases:
objectRender a South Indian style Vedic chart as SVG.
Usage:
renderer = SouthIndianRenderer(size=500, theme="classic") svg_string = renderer.render(chart) # Or save directly: renderer.render_to_file(chart, "vedic_south.svg")
Returns (stellium.returns)¶
Solar, lunar, and planetary return calculations.
ReturnBuilder: Fluent builder for planetary return charts.
A “return” is a chart cast for the moment when a transiting planet returns to its exact natal position. Common returns include: - Solar Return: Sun returns to natal Sun position (~birthday) - Lunar Return: Moon returns to natal Moon position (~monthly) - Saturn Return: Saturn returns to natal Saturn (~age 29, 58)
ReturnBuilder wraps ChartBuilder using composition, delegating all chainable configuration methods while handling the return-specific calculations internally.
- class stellium.returns.builder.ReturnBuilder(natal, planet, *, year=None, near_date=None, occurrence=None, location=None)[source]
Bases:
objectFluent builder for planetary return charts.
Uses composition: wraps ChartBuilder rather than inheriting from it. This allows us to: - Lazily calculate the return moment before building the inner chart - Inject return-specific metadata into the final chart - Delegate all chainable methods without tight coupling
- Usage:
>>> # Solar Return for 2025 >>> sr = ReturnBuilder.solar(natal_chart, 2025).calculate() >>> >>> # Lunar Return near a date >>> lr = ReturnBuilder.lunar(natal_chart, near_date="2025-03-15").calculate() >>> >>> # First Saturn Return >>> saturn = ReturnBuilder.planetary(natal_chart, "Saturn", occurrence=1).calculate() >>> >>> # Relocated Solar Return >>> sr_relocated = ( ... ReturnBuilder.solar(natal_chart, 2025, location="Tokyo, Japan") ... .calculate() ... )
- add_analyzer(analyzer)[source]
Add a chart analyzer.
- Return type:
- add_component(component)[source]
Add a calculation component.
- Return type:
- add_house_system(engine)[source]
Add an additional house system.
- Return type:
- calculate()[source]
Calculate the return chart.
This: 1. Finds the exact moment of the planetary return 2. Creates a ChartBuilder for that moment 3. Applies any deferred configuration 4. Injects return metadata 5. Returns the calculated chart
- Return type:
- Returns:
CalculatedChart with return metadata in chart.metadata
- classmethod lunar(natal, *, near_date=None, occurrence=None, location=None)[source]
Create a Lunar Return builder.
A Lunar Return is the chart cast for when the Moon returns to its exact natal position. This happens approximately every 27.3 days.
- Parameters:
- Return type:
- Returns:
ReturnBuilder configured for Lunar Return
Example
>>> # Lunar Return nearest to March 15, 2025 >>> lr = ReturnBuilder.lunar(natal, near_date="2025-03-15").calculate() >>> >>> # The 100th Lunar Return >>> lr_100 = ReturnBuilder.lunar(natal, occurrence=100).calculate()
- classmethod planetary(natal, planet, *, near_date=None, occurrence=None, location=None)[source]
Create a planetary return builder for any planet.
- Parameters:
- Return type:
- Returns:
ReturnBuilder configured for the specified planetary return
Example
>>> # First Saturn Return (~age 29) >>> sr1 = ReturnBuilder.planetary(natal, "Saturn", occurrence=1).calculate() >>> >>> # Jupiter Return nearest to 2025 >>> jr = ReturnBuilder.planetary( ... natal, "Jupiter", near_date="2025-06-01" ... ).calculate()
- classmethod solar(natal, year, *, location=None)[source]
Create a Solar Return builder.
A Solar Return is the chart cast for when the Sun returns to its exact natal position. This happens approximately on your birthday each year (but the exact time varies).
- Parameters:
natal (
CalculatedChart) – The natal chartyear (
int) – Year to calculate the return forlocation (
str|tuple[float,float] |ChartLocation|None) – Override location (for relocated Solar Return)
- Return type:
- Returns:
ReturnBuilder configured for Solar Return
Example
>>> sr_2025 = ReturnBuilder.solar(natal, 2025).calculate()
- with_aspects(engine=None)[source]
Set the aspect engine.
- Return type:
- with_config(config)[source]
Set the calculation configuration.
- Return type:
- with_ephemeris(engine)[source]
Set the ephemeris engine.
- Return type:
- with_house_systems(engines)[source]
Set the house system engines.
- Return type:
- with_orbs(engine=None)[source]
Set the orb engine.
- Return type:
Utilities (stellium.utils)¶
Utility functions and helpers.
Caching utilities for expensive operations like Swiss Ephemeris and geocoding.
- class stellium.utils.cache.Cache(cache_dir='.cache', max_age_seconds=86400, enabled=True)[source]
Bases:
objectFile-based cache for expensive operations.
- clear(cache_type=None)[source]
Clear cache entries. Returns number of files removed.
- Return type:
- get(cache_type, key)[source]
Get a value from cache if it exists and is not expired.
- get_stats()[source]
Get comprehensive cache statistics.
- Returns:
total_files: Number of cached files
total_size_bytes: Total cache size
by_type: Breakdown by cache type
hit_rate: Cache hit rate (if tracking enabled)
- Return type:
Dictionary with cache statistics including
- stellium.utils.cache.cache_info()[source]
Get comprehensive cache information.
- stellium.utils.cache.cache_size(cache_type=None)[source]
Get cache size information.
- stellium.utils.cache.cached(cache_type='general', max_age_seconds=86400, cache_instance=None)[source]
Decorator to cache function results.
- stellium.utils.cache.clear_cache(cache_type=None)[source]
Clear cache entries. Returns number of files removed.
- Return type:
- stellium.utils.cache.get_default_cache()[source]
Get the default global cache instance.
- Return type:
Cache
Cache utilities for easy access from within stellium package.
- stellium.utils.cache_utils.clear_all_cache()[source]
Clear all cache files.
- stellium.utils.cache_utils.clear_ephemeris_cache()[source]
Clear only the ephemeris cache.
- stellium.utils.cache_utils.clear_geocoding_cache()[source]
Clear only the geocoding cache.
- stellium.utils.cache_utils.get_cache_stats()[source]
Get cache statistics as a dictionary.
- stellium.utils.cache_utils.print_cache_info()[source]
Print detailed cache information.
Time and Julian Day conversion utilities.
These utilities handle conversion between Python datetime objects and Julian Day numbers, which are used internally by Swiss Ephemeris for all astronomical calculations.
- stellium.utils.time.datetime_to_julian_day(datetime_obj)[source]
Convert a Python datetime to Julian Day (UT).
- Parameters:
datetime_obj (
datetime) – A timezone-aware datetime object. If naive (no timezone), UTC is assumed.- Return type:
- Returns:
Julian Day number (Universal Time)
Example
>>> from datetime import datetime >>> import pytz >>> dt = datetime(2025, 1, 6, 12, 0, 0, tzinfo=pytz.UTC) >>> jd = datetime_to_julian_day(dt) >>> print(f"{jd:.6f}") # ~2460682.0
- stellium.utils.time.julian_day_to_datetime(jd, timezone='UTC')[source]
Convert Julian Day to Python datetime.
- Parameters:
- Return type:
- Returns:
Timezone-aware datetime object
Example
>>> jd = 2460682.0 # Noon on Jan 6, 2025 >>> dt_obj = julian_day_to_datetime(jd) >>> print(dt_obj.strftime("%Y-%m-%d %H:%M")) 2025-01-06 12:00
- stellium.utils.time.offset_julian_day(jd, days)[source]
Offset a Julian Day by a number of days.
- Parameters:
- Return type:
- Returns:
New Julian Day number
Example
>>> jd = 2460682.0 # Jan 6, 2025 >>> jd_tomorrow = offset_julian_day(jd, 1.0) >>> jd_yesterday = offset_julian_day(jd, -1.0)
Chart shape detection utilities.
Identifies the overall pattern/distribution of planets in a chart. Classic chart shapes include: Bundle, Bowl, Bucket, Locomotive, Seesaw, Splay, and Splash.
- stellium.utils.chart_shape.detect_chart_shape(chart)[source]
Detect the overall shape/pattern of planets in a chart.
- Parameters:
chart (
CalculatedChart) – Calculated chart- Return type:
tuple[Literal['Bundle','Bowl','Bucket','Locomotive','Seesaw','Splay','Splash'],dict]- Returns:
Tuple of (shape_name, metadata_dict) where metadata contains additional info like span, leading planet, handle planet, etc.
- stellium.utils.chart_shape.get_chart_shape_description(shape, metadata)[source]
Get a human-readable description of a chart shape.
Progression calculation utilities.
Supports multiple progression types, each using a different time key:
Secondary (day-for-a-year): 1 day of motion = 1 year of life. The most common type. To find progressions at age 30, look at day 30.
Tertiary (day-for-a-lunar-month): 1 day of motion = 1 lunar month (~27.3 days). Faster-moving, useful for timing within a year.
Minor (lunar-month-for-a-year): 1 lunar month of motion = 1 year of life. Intermediate rate between secondary and tertiary.
This module provides: - Progressed datetime calculation for all three types - Angle adjustment methods (Solar Arc, Naibod)
- stellium.utils.progressions.adjust_angles_by_arc(positions, arc)[source]
Adjust angle positions (ASC, MC, etc.) by a given arc.
Used for Solar Arc and Naibod angle progressions. In these methods, angles are moved forward by the calculated arc rather than using their natural (quotidian) progressed positions.
- Parameters:
positions (
tuple[CelestialPosition,...]) – Tuple of celestial positions from progressed chartarc (
float) – Arc in degrees to add to angle positions
- Return type:
- Returns:
New tuple with adjusted angle positions
- stellium.utils.progressions.calculate_lunar_arc(natal_moon_longitude, progressed_moon_longitude)[source]
Calculate lunar arc (difference between progressed Moon and natal Moon).
Lunar arc directions move all points at the rate the progressed Moon moves. The Moon moves ~12-13 degrees per year in progressions.
- Parameters:
- Return type:
- Returns:
Lunar arc in degrees (normalized to 0-360)
Example
>>> arc = calculate_lunar_arc(150.0, 280.0) # Moon moved ~130° >>> print(f"Lunar arc: {arc:.2f}°")
- stellium.utils.progressions.calculate_naibod_arc(years_elapsed)[source]
Calculate Naibod arc (mean Sun rate: 59’08” per year).
Naibod uses the Sun’s average daily motion to progress angles, rather than the actual progressed Sun position. This gives a consistent, predictable rate of angle progression.
- Parameters:
years_elapsed (
float) – Years since birth- Return type:
- Returns:
Naibod arc in degrees
Example
>>> arc = calculate_naibod_arc(30) # At age 30 >>> print(f"Naibod arc: {arc:.2f}°") # ~29.57°
- stellium.utils.progressions.calculate_planetary_arc(natal_planet_longitude, progressed_planet_longitude)[source]
Calculate arc based on any planet’s motion.
This generic function supports Mars arc, Venus arc, Jupiter arc, etc. Used for custom planetary arcs and chart ruler arcs.
- Parameters:
- Return type:
- Returns:
Planetary arc in degrees (normalized to 0-360)
Example
>>> arc = calculate_planetary_arc(45.0, 75.0) # Planet moved ~30° >>> print(f"Planet arc: {arc:.2f}°")
- stellium.utils.progressions.calculate_progressed_datetime(natal_datetime, target_date, progression_type='secondary')[source]
Calculate progressed datetime using the appropriate time key.
Each progression type maps real elapsed time to symbolic chart time at a different rate:
Secondary (day-for-a-year): 1 real year → 1 progressed day. At age 30, look at day 30 after birth.
Tertiary (day-for-a-lunar-month): 1 real lunar month → 1 progressed day. Moves ~13.4x faster than secondary.
Minor (lunar-month-for-a-year): 1 real year → 1 progressed lunar month. Moves ~27.3x faster than secondary.
- Parameters:
- Return type:
- Returns:
The datetime to cast the progressed chart for
Example
>>> from datetime import datetime >>> birth = datetime(1994, 1, 6, 11, 47) >>> target = datetime(2024, 1, 6) # 30th birthday >>> # Secondary: ~30 days after birth >>> progressed = calculate_progressed_datetime(birth, target) >>> # Tertiary: ~401 days after birth (30 years × 13.4 lunar months/year) >>> progressed = calculate_progressed_datetime(birth, target, "tertiary")
- stellium.utils.progressions.calculate_solar_arc(natal_sun_longitude, progressed_sun_longitude)[source]
Calculate solar arc (difference between progressed Sun and natal Sun).
Solar arc is used to progress angles (ASC, MC) at the same rate as the progressed Sun moves.
- Parameters:
- Return type:
- Returns:
Solar arc in degrees (always positive, 0-360)
Example
>>> arc = calculate_solar_arc(285.5, 315.2) # Sun moved ~30° >>> print(f"Solar arc: {arc:.2f}°")
- stellium.utils.progressions.calculate_years_elapsed(natal_datetime, target_date)[source]
Calculate years elapsed between natal date and target date.
- stellium.utils.progressions.normalize_arc(arc)[source]
Normalize an arc to the range 0-360 degrees.
Chart ruler calculation utilities.
The chart ruler is the planet that rules the Ascendant sign.
- stellium.utils.chart_ruler.get_chart_ruler(ascendant_sign, system='traditional')[source]
Get the chart ruler based on the Ascendant sign.
The chart ruler is the planet that rules the rising sign. It is considered one of the most important planets in the natal chart.
- Parameters:
- Return type:
- Returns:
The name of the chart ruler planet
Example
>>> get_chart_ruler("Leo") 'Sun' >>> get_chart_ruler("Aquarius", system="modern") 'Uranus' >>> get_chart_ruler("Aquarius", system="traditional") 'Saturn'
- stellium.utils.chart_ruler.get_chart_ruler_from_chart(chart, system='traditional')[source]
Get the chart ruler from a CalculatedChart object.
- Parameters:
chart – A CalculatedChart instance
system (
Literal['traditional','modern']) – “traditional” or “modern” rulership system
- Return type:
- Returns:
A tuple of (ruler_planet_name, ascendant_sign)
Example
>>> chart = ChartBuilder.from_notable("Kate Louie").calculate() >>> ruler, sign = get_chart_ruler_from_chart(chart) >>> print(f"Chart ruler: {ruler} (ruling {sign} rising)") Chart ruler: Sun (ruling Leo rising)
- stellium.utils.chart_ruler.get_sign_ruler(sign, system='traditional')[source]
Get the planetary ruler of a zodiac sign.
- Parameters:
- Return type:
- Returns:
The name of the ruling planet
Example
>>> get_sign_ruler("Aries") 'Mars' >>> get_sign_ruler("Scorpio", system="modern") 'Pluto' >>> get_sign_ruler("Scorpio", system="traditional") 'Mars'
House calculation utilities.
- stellium.utils.houses.find_house_for_longitude(longitude, cusps)[source]
Find which house a longitude falls into.
This function determines which of the 12 houses a given ecliptic longitude occupies, based on the house cusp positions. It correctly handles houses that wrap around the 360°/0° boundary.
- Parameters:
- Return type:
- Returns:
House number (1-12)
Example
>>> cusps = (0.0, 30.0, 60.0, 90.0, 120.0, 150.0, ... 180.0, 210.0, 240.0, 270.0, 300.0, 330.0) >>> find_house_for_longitude(45.0, cusps) 2 >>> find_house_for_longitude(355.0, cusps) # Wraps around 12
Note
A planet at the exact cusp belongs to the house it’s entering, not the one it’s leaving. The logic uses cusp_start <= longitude < cusp_end.
Utility functions for finding planetary crossings.
A “crossing” is when a planet reaches a specific zodiacal longitude. Used for returns, ingresses, and other timing techniques.
- stellium.utils.planetary_crossing.find_nth_return(planet, natal_longitude, birth_jd, n=1)[source]
Find the Nth planetary return after birth.
A “return” is when a transiting planet returns to its natal position.
- Parameters:
- Return type:
- Returns:
Julian Day of the Nth return
- Raises:
ValueError – If n < 1 or planet not found
Example
>>> # Find first Saturn return (~age 29) >>> birth_jd = 2449718.0 # Jan 6, 1994 >>> natal_saturn = 330.5 # Saturn's natal position >>> sr1 = find_nth_return("Saturn", natal_saturn, birth_jd, n=1)
- stellium.utils.planetary_crossing.find_planetary_crossing(planet, target_longitude, start_jd, direction=1, precision=1e-06)[source]
Find the Julian Day when a planet reaches a target longitude.
Uses a two-phase algorithm: 1. Coarse search: Step forward/backward until we bracket the crossing 2. Binary search: Refine to sub-second precision
- Parameters:
planet (
str) – Planet name (must be in CELESTIAL_REGISTRY)target_longitude (
float) – Target ecliptic longitude (0-360)start_jd (
float) – Julian Day to start searching fromdirection (
int) – 1 for forward in time, -1 for backwardprecision (
float) – Desired precision in Julian Days (default ~0.08 seconds)
- Return type:
- Returns:
Julian Day of the crossing
- Raises:
ValueError – If planet not found or crossing not found within bounds
Example
>>> # Find when Sun reaches 15° Capricorn after Jan 1, 2025 >>> from stellium.utils.time import datetime_to_julian_day >>> from datetime import datetime >>> start = datetime_to_julian_day(datetime(2025, 1, 1)) >>> jd = find_planetary_crossing("Sun", 285.0, start) # 285° = 15° Cap
- stellium.utils.planetary_crossing.find_return_near_date(planet, natal_longitude, target_jd)[source]
Find the planetary return nearest to a target date.
Searches both forward and backward, returns the closer one. Note: For retrograde planets, this may return a retrograde crossing.
- Parameters:
- Return type:
- Returns:
Julian Day of the nearest return
Example
>>> # Find lunar return nearest to March 15, 2025 >>> from stellium.utils.time import datetime_to_julian_day >>> from datetime import datetime >>> target = datetime_to_julian_day(datetime(2025, 3, 15)) >>> natal_moon = 105.5 >>> lr = find_return_near_date("Moon", natal_moon, target)
Data (stellium.data)¶
Data access and notable births registry.
- stellium.data.get_notable_registry()[source]¶
Get the global notable registry instance (lazy import).
- stellium.data.get_ephe_dir()[source]¶
Get the ephemeris directory, initializing if necessary.
This is the main function that should be used throughout the codebase to get the ephemeris path. Respects any override previously set via
initialize_ephemeris()or theSTELLIUM_EPHE_PATHenv var.- Return type:
- Returns:
Path to the ephemeris directory currently in use.
- stellium.data.get_user_data_dir()[source]¶
Get the user data directory, creating it if necessary.
- Return type:
- Returns:
Path to ~/.stellium/
- stellium.data.get_user_ephe_dir()[source]¶
Get the user ephemeris directory, creating it if necessary.
- Return type:
- Returns:
Path to ~/.stellium/ephe/
- stellium.data.has_ephe_file(filename)[source]¶
Check if a specific ephemeris file exists in the active directory.
- stellium.data.initialize_ephemeris(ephe_path=None)[source]¶
Initialize the ephemeris system.
This function: 1. Resolves which ephemeris directory to use (explicit arg >
STELLIUM_EPHE_PATHenv var > default~/.stellium/ephe/)For the default location: ensures the directory exists and copies bundled ephemeris files to it (first run only)
Sets the Swiss Ephemeris path via
swe.set_ephe_path
When a custom path is supplied the directory is used as-is: Stellium will not create it or copy its bundled files into it. This makes it safe to point at an existing Swiss Ephemeris installation managed by another tool, or at a read-only folder.
If
initialize_ephemerisis called a second time with a different path, the ephemeris is re-initialized against the new location.
Paths (stellium.data.paths)¶
Centralized data path management for Stellium.
This module handles: 1. User data directory (~/.stellium/) for ephemeris files and user data 2. Bundled package data (notables, essential ephemeris files) 3. First-run initialization (copying bundled ephemeris to user directory)
- The user directory structure:
~/.stellium/ ├── ephe/ # Swiss Ephemeris files (copied from package + user downloads) │ ├── sepl_18.se1 │ ├── semo_18.se1 │ └── … └── cache/ # Future: cache files
- stellium.data.paths.get_ephe_dir()[source]
Get the ephemeris directory, initializing if necessary.
This is the main function that should be used throughout the codebase to get the ephemeris path. Respects any override previously set via
initialize_ephemeris()or theSTELLIUM_EPHE_PATHenv var.- Return type:
- Returns:
Path to the ephemeris directory currently in use.
- stellium.data.paths.get_user_data_dir()[source]
Get the user data directory, creating it if necessary.
- Return type:
- Returns:
Path to ~/.stellium/
- stellium.data.paths.get_user_ephe_dir()[source]
Get the user ephemeris directory, creating it if necessary.
- Return type:
- Returns:
Path to ~/.stellium/ephe/
- stellium.data.paths.has_ephe_file(filename)[source]
Check if a specific ephemeris file exists in the active directory.
- stellium.data.paths.initialize_ephemeris(ephe_path=None)[source]
Initialize the ephemeris system.
This function: 1. Resolves which ephemeris directory to use (explicit arg >
STELLIUM_EPHE_PATHenv var > default~/.stellium/ephe/)For the default location: ensures the directory exists and copies bundled ephemeris files to it (first run only)
Sets the Swiss Ephemeris path via
swe.set_ephe_path
When a custom path is supplied the directory is used as-is: Stellium will not create it or copy its bundled files into it. This makes it safe to point at an existing Swiss Ephemeris installation managed by another tool, or at a read-only folder.
If
initialize_ephemerisis called a second time with a different path, the ephemeris is re-initialized against the new location.
Notable Registry (stellium.data.registry)¶
Registry for curated notable births and events.
Provides access to a compendium of famous births and historical events that can be used for examples, testing, and research.
- class stellium.data.registry.NotableRegistry[source]
Bases:
objectRegistry for curated notable births and events.
This provides access to a compendium of famous births and historical events that can be used for examples, testing, and research.
Example
>>> from stellium.data import get_notable_registry >>> registry = get_notable_registry() >>> einstein = registry.get_by_name("Albert Einstein") >>> print(einstein.name, einstein.category) Albert Einstein scientist
- get_by_category(category)[source]
Get all notables in a category.
- get_by_event_type(event_type)[source]
Get all births or all events.
- get_by_name(name)[source]
Get notable by name (case-insensitive).
- search(**filters)[source]
Search with arbitrary filters.
Examples
>>> registry.search(category="scientist") >>> registry.search(event_type="birth", verified=True) >>> registry.search(data_quality="AA")
- stellium.data.registry.get_notable_registry()[source]
Get the global notable registry instance.
- Return type:
NotableRegistry- Returns:
The singleton NotableRegistry instance
Example
>>> registry = get_notable_registry() >>> einstein = registry.get_by_name("Albert Einstein")
Planner (stellium.planner)¶
PDF planner generation with astrological data.
PlannerBuilder - Fluent API for creating personalized astrological planners.
This module provides a builder pattern for configuring and generating PDF planners with charts, transits, and daily astrological events.
- class stellium.planner.builder.PlannerBuilder(native)[source]
Bases:
objectFluent builder for creating personalized astrological planners.
Example
>>> from stellium import Native >>> from stellium.planner import PlannerBuilder >>> >>> native = Native("1990-05-15 14:30", "San Francisco, CA") >>> planner = (PlannerBuilder.for_native(native) ... .year(2025) ... .timezone("America/Los_Angeles") ... .with_natal_chart() ... .with_solar_return() ... .include_natal_transits() ... .generate("my_planner.pdf"))
- binding_margin(inches)[source]
Add extra margin for binding.
- Parameters:
inches (
float) – Extra margin in inches (added to inner edge)- Return type:
- Returns:
Self for chaining
- date_range(start, end)[source]
Set a custom date range for the planner.
- Parameters:
- Return type:
- Returns:
Self for chaining
- exclude_voc()[source]
Exclude Void of Course Moon periods.
- Return type:
- classmethod for_native(native)[source]
Start building a planner for a native.
- Parameters:
native (
Native) – The Native whose planner to create- Return type:
- Returns:
PlannerBuilder instance for chaining
- generate(output_path=None)[source]
Generate the PDF planner.
- include_ingresses(planets=None)[source]
Include planet sign ingresses.
- include_moon_phases(enabled=True)[source]
Include Moon phases (new, full, quarters).
- Return type:
- include_mundane_transits(enabled=True)[source]
Include mundane transits (planet-to-planet in sky).
- Return type:
- include_natal_transits(planets=None)[source]
Include transits to natal planets.
- include_stations(planets=None)[source]
Include retrograde/direct stations.
- include_voc(mode='traditional')[source]
Include Void of Course Moon periods.
- Parameters:
mode (
Literal['traditional','modern']) – “traditional” (Sun-Saturn) or “modern” (includes outer planets)- Return type:
- Returns:
Self for chaining
- location(location)[source]
Set location for angle calculations and planetary hours.
Defaults to the native’s birth location if not specified.
- page_size(size)[source]
Set page size.
- Parameters:
size (
Literal['a4','a5','letter','half-letter']) – “a4” (default), “a5”, “letter”, or “half-letter” (alias for a5)- Return type:
- Returns:
Self for chaining
- timezone(tz)[source]
Set the timezone for transit times.
This is required - transit times will be displayed in this timezone.
- Parameters:
tz (
str) – Timezone string (e.g., “America/Los_Angeles”, “Europe/London”)- Return type:
- Returns:
Self for chaining
- week_starts_on(day)[source]
Set the first day of the week for calendar grids.
- Parameters:
day (
Literal['sunday','monday']) – “sunday” (default) or “monday”- Return type:
- Returns:
Self for chaining
- with_graphic_ephemeris(harmonic=360, enabled=True)[source]
Include graphic ephemeris for the planner period.
- Parameters:
- Return type:
- Returns:
Self for chaining
- with_natal_chart(enabled=True)[source]
Include natal chart wheel in front matter.
- Return type:
- with_profections(enabled=True)[source]
Include annual profection info (Lord of the Year).
- Return type:
- with_progressed_chart(enabled=True)[source]
Include secondary progressed chart in front matter.
- Return type:
- with_solar_return(enabled=True)[source]
Include solar return chart for the planner year.
- Return type:
- with_zr_timeline(lot='Part of Fortune', enabled=True)[source]
Include Zodiacal Releasing timeline visualization.
- Parameters:
- Return type:
- Returns:
Self for chaining
- class stellium.planner.builder.PlannerConfig(native, timezone, start_date=None, end_date=None, year=None, location=None, include_natal_chart=True, include_progressed_chart=True, include_solar_return=True, include_profections=True, include_zr_timeline=True, zr_lot='Part of Fortune', include_graphic_ephemeris=True, graphic_ephemeris_harmonic=360, natal_transit_planets=None, include_mundane_transits=True, include_moon_phases=True, include_voc=True, voc_mode='traditional', ingress_planets=None, station_planets=None, page_size='a4', binding_margin=0.0, week_starts_on='sunday')[source]
Bases:
objectConfiguration for planner generation.
- binding_margin: float = 0.0
- graphic_ephemeris_harmonic: int = 360
- include_graphic_ephemeris: bool = True
- include_moon_phases: bool = True
- include_mundane_transits: bool = True
- include_natal_chart: bool = True
- include_profections: bool = True
- include_progressed_chart: bool = True
- include_solar_return: bool = True
- include_voc: bool = True
- include_zr_timeline: bool = True
- native: Native
- page_size: Literal['a4', 'a5', 'letter', 'half-letter'] = 'a4'
- timezone: str
- voc_mode: Literal['traditional', 'modern'] = 'traditional'
- week_starts_on: Literal['sunday', 'monday'] = 'sunday'
- zr_lot: str = 'Part of Fortune'
PlannerRenderer - Generate beautiful PDF planners using Typst.
This module handles: - Generating front matter (charts, ZR timeline, graphic ephemeris) - Rendering daily pages with events - Typst compilation to PDF
- class stellium.planner.renderer.ChartPaths(natal=None, progressed=None, solar_return=None, graphic_ephemeris=None, zr_overview=None, zr_timeline=None, profection_wheel=None, profection_table=None)[source]
Bases:
objectPaths to generated chart SVG files.
- class stellium.planner.renderer.PlannerRenderer(config)[source]
Bases:
objectRenders PDF planners using Typst typesetting.
Generates: - Title page with planner info - Front matter section with charts - Month overview pages - Daily pages with transit events
DailyEventCollector - Gather astrological events for planner pages.
This module collects and organizes transits, ingresses, stations, Moon phases, and VOC periods for each day of the planner.
- class stellium.planner.events.DailyEvent(time, event_type, description, symbol, priority=3)[source]
Bases:
objectA single astrological event for display in the planner.
- time
Event time in the user’s timezone
- event_type
Category of event
- description
Human-readable description
- symbol
Glyph representation for compact display
- priority
Sorting priority (1=highest, 5=lowest)
- description: str
- event_type: Literal['transit_natal', 'transit_mundane', 'ingress', 'station', 'moon_phase', 'voc_start', 'voc_end', 'eclipse']
- priority: int = 3
- symbol: str
- time: datetime
- class stellium.planner.events.DailyEventCollector(natal_chart, start, end, timezone)[source]
Bases:
objectCollects all astrological events for a date range.
This class gathers events from various sources (transits, ingresses, stations, Moon phases, VOC periods) and organizes them by date.
Example
>>> collector = DailyEventCollector( ... natal_chart=chart, ... start=date(2025, 1, 1), ... end=date(2025, 12, 31), ... timezone="America/Los_Angeles" ... ) >>> collector.collect_all() >>> events = collector.get_events_for_day(date(2025, 1, 15))
- collect_all(natal_transits=True, transit_planets=None, ingresses=True, ingress_planets=None, stations=True, station_planets=None, moon_phases=True, voc=True, voc_mode='traditional', eclipses=True)[source]
Collect all configured event types.
- Parameters:
natal_transits (
bool) – Include transits to natal planetstransit_planets (
list[str] |None) – Which transiting planetsingresses (
bool) – Include sign ingressesingress_planets (
list[str] |None) – Which planets for ingressesstations (
bool) – Include retrograde/direct stationsstation_planets (
list[str] |None) – Which planets for stationsmoon_phases (
bool) – Include Moon phasesvoc (
bool) – Include VOC periodsvoc_mode (
Literal['traditional','modern']) – VOC calculation modeeclipses (
bool) – Include eclipses
- Return type:
- collect_ingresses(planets=None)[source]
Collect planet sign ingresses.
- collect_natal_transits(transit_planets=None, aspects=None)[source]
Collect transits from outer planets to natal planets.
Uses longitude crossing search to find when transit planets reach aspect positions to fixed natal planet longitudes.
- collect_stations(planets=None)[source]
Collect retrograde and direct stations.
- collect_voc_periods(mode='traditional')[source]
Collect Void of Course Moon periods.
Adds both start and end times for each VOC period.
- end: date
- get_events_for_day(day)[source]
Get all events for a specific day, sorted by time.
- natal_chart: CalculatedChart
- start: date
- timezone: str
Electional (stellium.electional)¶
Electional astrology tools for finding optimal times.
Time window generation and set operations for electional search optimization.
Instead of checking every time point, we pre-compute windows where conditions are true and intersect them. This transforms O(N) point-checks into fast set intersection math.
Example
>>> from stellium.electional.intervals import waxing_windows, intersect_windows
>>> from datetime import datetime
>>>
>>> # Get all waxing moon windows in 2025
>>> windows = waxing_windows(datetime(2025, 1, 1), datetime(2025, 12, 31))
>>> print(f"Found {len(windows)} waxing periods")
- class stellium.electional.intervals.TimeWindow(start_jd, end_jd)[source]
Bases:
objectA time interval where a condition is true.
TimeWindow stores times as Julian Day numbers, which are UTC-based. This is intentional for astronomical correctness and clean interval math.
Note
The
start_datetimeandend_datetimeproperties return naive datetimes in UTC. If you need local time, convert using pytz:import pytz local_tz = pytz.timezone("America/Los_Angeles") local_start = window.start_datetime.replace(tzinfo=pytz.UTC).astimezone(local_tz)
See also
ElectionWindow: User-facing result type that stores local datetimes.
- start_jd
Start of window as Julian Day (UTC-based)
- end_jd
End of window as Julian Day (UTC-based)
- property duration_days: float
Duration of the window in days.
- property duration_hours: float
Duration of the window in hours.
- property end_datetime: datetime
End as datetime (UTC).
- end_jd: float
- property start_datetime: datetime
Start as datetime (UTC).
- start_jd: float
- stellium.electional.intervals.angle_at_longitude_windows(target_longitude, latitude, longitude, angle, start, end, orb=1.0)[source]
Get windows when a chart angle is within orb of a specific longitude.
Since angles rotate with Earth’s rotation (~1° every 4 minutes), the window duration depends on the orb: - 1° orb → ~8 minute window - 3° orb → ~24 minute window
- Parameters:
target_longitude (
float) – Target longitude in degrees (0-360)latitude (
float) – Geographic latitudelongitude (
float) – Geographic longitude (negative = West)angle (
str) – Which angle (“ASC”, “MC”, “DSC”, “IC”)start (
datetime|float) – Start of search range (datetime or Julian day)end (
datetime|float) – End of search range (datetime or Julian day)orb (
float) – Maximum orb in degrees (default 1°)
- Return type:
list[TimeWindow]- Returns:
List of TimeWindow objects where angle is within orb of target
Example
>>> # Get windows when MC is within 1° of 0° Aries >>> windows = angle_at_longitude_windows( ... 0.0, 40.7, -74.0, "MC", ... datetime(2025, 1, 1), datetime(2025, 1, 8), orb=1.0 ... ) >>> for w in windows: ... print(f"{w.start_datetime} - {w.end_datetime}")
- stellium.electional.intervals.aspect_exact_windows(object1, object2, aspect_angle, start, end, orb=3.0)[source]
Get windows when two objects are within orb of exact aspect.
For each exact aspect in the range, computes the window where the aspect is within the specified orb. Uses the relative speed of the objects to calculate how long before and after exact the aspect stays within orb.
- Parameters:
object1 (
str) – First object name (e.g., “Moon”)object2 (
str) – Second object name (e.g., “Jupiter”)aspect_angle (
float) – Target angle (0=conjunction, 60=sextile, 90=square, 120=trine, 180=opposition)start (
datetime|float) – Start of search range (datetime or Julian day)end (
datetime|float) – End of search range (datetime or Julian day)orb (
float) – Maximum orb in degrees (default 3°)
- Return type:
list[TimeWindow]- Returns:
List of TimeWindow objects where aspect is within orb
Example
>>> # Get windows when Moon is within 2° of exact trine to Jupiter >>> windows = aspect_exact_windows("Moon", "Jupiter", 120.0, ... datetime(2025, 1, 1), datetime(2025, 2, 1), orb=2.0) >>> for w in windows: ... print(f"{w.start_datetime} - {w.end_datetime}")
- stellium.electional.intervals.direct_windows(planet, start, end)[source]
Get windows when a planet is direct (not retrograde).
- stellium.electional.intervals.intersect_windows(windows_a, windows_b)[source]
Compute intersection of two sorted window lists.
For each overlapping pair, emits the overlap: (max(start_a, start_b), min(end_a, end_b))
- stellium.electional.intervals.invert_windows(windows, start_jd, end_jd)[source]
Get complement windows (gaps between the given windows).
- stellium.electional.intervals.moon_sign_not_in_windows(signs, start, end)[source]
Get windows when Moon is NOT in specified signs.
- stellium.electional.intervals.moon_sign_windows(signs, start, end)[source]
Get windows when Moon is in specified signs.
- stellium.electional.intervals.not_voc_windows(start, end, mode='traditional')[source]
Get windows when Moon is NOT void of course.
- stellium.electional.intervals.retrograde_windows(planet, start, end)[source]
Get windows when a planet is retrograde.
- stellium.electional.intervals.union_windows(windows_a, windows_b)[source]
Merge two window lists, combining overlapping windows.
- stellium.electional.intervals.voc_windows(start, end, mode='traditional')[source]
Get windows when Moon is void of course.
A void of course Moon has made its last major Ptolemaic aspect (conjunction, sextile, square, trine, opposition) before leaving its current sign.
This implementation uses the actual VOC calculation engine for accuracy, with binary search to find VOC transition times.
- stellium.electional.intervals.waning_windows(start, end)[source]
Get windows when Moon is waning (from Full Moon to New Moon).
- stellium.electional.intervals.waxing_windows(start, end)[source]
Get windows when Moon is waxing (from New Moon to Full Moon).
Planetary Hours calculation for electional astrology.
Planetary hours are a traditional timing system where each hour of the day is ruled by one of the seven classical planets in the Chaldean order.
The day is divided into 12 “hours” from sunrise to sunset (variable length), and night into 12 “hours” from sunset to sunrise. The first hour of each day is ruled by the planet that rules that weekday.
Chaldean order (from slowest to fastest): Saturn → Jupiter → Mars → Sun → Venus → Mercury → Moon
Day rulers: - Sunday: Sun - Monday: Moon - Tuesday: Mars - Wednesday: Mercury - Thursday: Jupiter - Friday: Venus - Saturday: Saturn
- Example usage:
>>> from stellium.electional.planetary_hours import get_planetary_hour, get_planetary_hours_for_day >>> hour = get_planetary_hour(datetime.now(), latitude=37.7, longitude=-122.4) >>> print(f"Current planetary hour: {hour.ruler}")
>>> hours = get_planetary_hours_for_day(datetime(2025, 1, 1), latitude=37.7, longitude=-122.4) >>> for h in hours[:3]: ... print(f"{h.ruler}: {h.start_local} - {h.end_local}")
- class stellium.electional.planetary_hours.PlanetaryHour(ruler, hour_number, is_day_hour, start_jd, end_jd, start_utc, end_utc)[source]
Bases:
objectA single planetary hour.
- ruler
The planet ruling this hour
- hour_number
Hour number (1-12 for day, 13-24 for night)
- is_day_hour
True if this is a day hour (sunrise to sunset)
- start_jd
Start time as Julian day
- end_jd
End time as Julian day
- start_utc
Start time as UTC datetime
- end_utc
End time as UTC datetime
- property duration_minutes: float
Duration of this hour in clock minutes.
- end_jd: float
- end_utc: datetime
- hour_number: int
- is_day_hour: bool
- ruler: str
- start_jd: float
- start_utc: datetime
- stellium.electional.planetary_hours.get_day_ruler(date)[source]
Get the planetary ruler of a given day.
- stellium.electional.planetary_hours.get_hour_ruler(day_ruler, hour_number)[source]
Get the planetary ruler of a specific hour.
- stellium.electional.planetary_hours.get_planetary_hour(dt, latitude, longitude, altitude=0.0)[source]
Get the planetary hour for a specific datetime.
- stellium.electional.planetary_hours.get_planetary_hour_at_jd(jd, latitude, longitude, altitude=0.0)[source]
Get the planetary hour for a specific Julian day.
- stellium.electional.planetary_hours.get_planetary_hours_for_day(date, latitude, longitude, altitude=0.0)[source]
Get all 24 planetary hours for a given day.
Returns hours from sunrise of the given date to sunrise of the next date.
- stellium.electional.planetary_hours.get_sunrise_sunset(date, latitude, longitude, altitude=0.0)[source]
Get sunrise and sunset times for a given LOCAL date and location.
The “date” is interpreted as the local calendar date at the given longitude. For example, if date is Jan 1, 2025 and longitude is -122° (San Francisco), this returns the sunrise/sunset that occur on Jan 1 in Pacific time.
- Parameters:
- Return type:
- Returns:
Tuple of (sunrise_jd, sunset_jd) as Julian day numbers
- stellium.electional.planetary_hours.planetary_hour_windows(planet, latitude, longitude, start, end)[source]
Get all windows when a specific planet rules the planetary hour.
- Parameters:
planet (
str) – Planet name (“Sun”, “Moon”, “Mars”, “Mercury”, “Jupiter”, “Venus”, “Saturn”)latitude (
float) – Geographic latitudelongitude (
float) – Geographic longitudestart (
datetime|float) – Start of search range (datetime or Julian day)end (
datetime|float) – End of search range (datetime or Julian day)
- Return type:
- Returns:
List of (start_jd, end_jd) tuples for each planetary hour of that planet
Helper predicates for electional astrology conditions.
These factory functions return Condition callables that can be used with ElectionalSearch. They provide readable, reusable building blocks for common astrological filters.
All predicates return Callable[[CalculatedChart], bool] (the Condition type).
Each predicate is tagged with a “speed hint” indicating how quickly the condition changes, enabling hierarchical filtering for performance: - SPEED_DAY: Stable conditions (phase, retrograde) - checked once at noon - SPEED_DAY_SIGN: Sign-based conditions - checked at start+end of day - SPEED_HOUR: Hour-level conditions (VOC, aspects) - SPEED_MINUTE: House/angular positions (change with Earth’s rotation)
Example
>>> from stellium.electional import ElectionalSearch, is_waxing, not_voc, on_angle
>>> results = (ElectionalSearch("2025-01-01", "2025-12-31", "New York, NY")
... .where(is_waxing())
... .where(not_voc())
... .where(on_angle("Jupiter"))
... .find_moments())
- stellium.electional.predicates.angle_at_degree(target_longitude, angle='ASC', orb=1.0)[source]
Chart angle is within orb of a specific zodiac degree.
This predicate checks if a chart angle (ASC, MC, DSC, IC) is within the specified orb of a target longitude. Useful for finding moments when specific degrees rise or culminate.
- Parameters:
- Return type:
- Returns:
Condition that checks if angle is within orb of target
Example
>>> # Find moments when 0° Aries is rising >>> search.where(angle_at_degree(0.0, "ASC", orb=1.0))
>>> # Find moments when 15° Leo is culminating >>> search.where(angle_at_degree(135.0, "MC", orb=0.5))
- stellium.electional.predicates.aspect_applying(obj1, obj2, aspects=None, orb_max=None)[source]
Applying aspect between two objects.
An applying aspect is getting tighter (objects moving toward exact aspect).
- Parameters:
- Return type:
- Returns:
Condition that checks for applying aspect between the objects
- stellium.electional.predicates.aspect_exact_within(obj1, obj2, aspect, orb=1.0)[source]
Aspect between objects is within orb of exact.
This predicate checks if two objects are within a tight orb of an exact aspect. Useful for finding moments near perfection of an aspect.
- Parameters:
- Return type:
- Returns:
Condition that checks if aspect is within orb of exact
Example
>>> # Find moments when Moon is within 0.5° of exact trine to Jupiter >>> search.where(aspect_exact_within("Moon", "Jupiter", "trine", orb=0.5))
- stellium.electional.predicates.aspect_separating(obj1, obj2, aspects=None, orb_max=None)[source]
Separating aspect between two objects.
A separating aspect is getting looser (objects moving away from exact aspect).
- Parameters:
- Return type:
- Returns:
Condition that checks for separating aspect between the objects
- stellium.electional.predicates.cadent(name)[source]
Object is in a cadent house (3, 6, 9, 12).
- Parameters:
name (
str) – Object name- Return type:
- Returns:
Condition that checks if object is in a cadent house
- stellium.electional.predicates.get_speed_hint(condition)[source]
Get the speed hint for a condition, defaulting to SPEED_MINUTE.
- Return type:
- stellium.electional.predicates.get_window_generator(condition)[source]
Get the window generator for a condition, if available.
- stellium.electional.predicates.has_aspect(obj1, obj2, aspects=None, orb_max=None)[source]
Objects are in aspect (regardless of applying/separating).
- Parameters:
- Return type:
- Returns:
Condition that checks if objects are in aspect
- stellium.electional.predicates.in_house(name, houses)[source]
Object is in one of the specified houses.
- stellium.electional.predicates.in_planetary_hour(planet)[source]
Check if the current time is in a planetary hour ruled by the specified planet.
Planetary hours are a traditional timing system where each hour of the day is ruled by one of the seven classical planets in Chaldean order.
- Parameters:
planet (
str) – Planet name (“Sun”, “Moon”, “Mars”, “Mercury”, “Jupiter”, “Venus”, “Saturn”)- Return type:
- Returns:
Condition that checks if current time is in that planet’s hour
Example
>>> # Find Jupiter hours (good for expansion, luck, legal matters) >>> search.where(in_planetary_hour("Jupiter"))
>>> # Find Venus hours on Friday for love matters >>> search.where(in_planetary_hour("Venus"))
- stellium.electional.predicates.is_combust(name, orb=8.5)[source]
Planet is combust (too close to the Sun).
A combust planet is weakened by proximity to the Sun.
- Parameters:
- Return type:
- Returns:
Condition that checks if planet is combust
- stellium.electional.predicates.is_debilitated(name, debilities=None, system='traditional')[source]
Planet is debilitated (in detriment or fall).
- Parameters:
- Return type:
- Returns:
Condition that checks if planet is debilitated
- stellium.electional.predicates.is_dignified(name, dignities=None, system='traditional')[source]
Planet has essential dignity.
Essential dignity means the planet is strengthened by its sign position: - ruler: Planet rules the sign (e.g., Mars in Aries) - exaltation: Planet is exalted (e.g., Sun in Aries) - triplicity: Planet rules the element (depends on sect) - bound/term: Planet rules the degree range - decan/face: Planet rules the 10° section
- Parameters:
- Return type:
- Returns:
Condition that checks if planet has specified dignities
- stellium.electional.predicates.is_out_of_bounds(name)[source]
Object is out of bounds (declination beyond ~23.4°).
Out of bounds planets are considered to operate outside normal rules.
- Parameters:
name (
str) – Object name- Return type:
- Returns:
Condition that checks if object is out of bounds
- stellium.electional.predicates.is_retrograde(name)[source]
Planet is retrograde.
- Parameters:
name (
str) – Planet name (e.g., “Mercury”, “Venus”, “Mars”)- Return type:
- Returns:
Condition that checks if planet is retrograde
- stellium.electional.predicates.is_voc(mode='traditional')[source]
Moon is void of course.
A void of course Moon has no major aspects before leaving its current sign.
- Parameters:
mode (
str) – “traditional” (Sun-Saturn) or “modern” (includes outer planets)- Return type:
- Returns:
Condition that checks if Moon is void of course
- stellium.electional.predicates.is_waning()[source]
Moon is waning (between Full and New Moon).
- Return type:
- Returns:
Condition that checks if Moon phase is waning
- stellium.electional.predicates.is_waxing()[source]
Moon is waxing (between New and Full Moon).
- Return type:
- Returns:
Condition that checks if Moon phase is waxing
Example
>>> search.where(is_waxing())
- stellium.electional.predicates.moon_phase(phases)[source]
Moon is in one of the specified phases.
- Parameters:
phases (
list[str]) – List of phase names, e.g., [“New Moon”, “Full Moon”] Valid phases: “New Moon”, “Waxing Crescent”, “First Quarter”, “Waxing Gibbous”, “Full Moon”, “Waning Gibbous”, “Last Quarter”, “Waning Crescent”- Return type:
- Returns:
Condition that checks if Moon phase matches any in the list
- stellium.electional.predicates.no_aspect(obj1, obj2, aspects=None, orb_max=None)[source]
Objects are NOT in aspect.
- Parameters:
- Return type:
- Returns:
Condition that checks if objects are NOT in aspect
- stellium.electional.predicates.no_hard_aspect(name, exclude_objects=None, applying_only=True)[source]
Object has no hard aspects (square, opposition) from any planet.
Hard aspects from malefics (Mars, Saturn) are particularly problematic in electional astrology.
- Parameters:
- Return type:
- Returns:
Condition that checks object has no hard aspects
- stellium.electional.predicates.no_malefic_aspect(name, applying_only=True)[source]
Object has no hard aspects from Mars or Saturn.
- Parameters:
- Return type:
- Returns:
Condition that checks object has no Mars/Saturn hard aspects
- stellium.electional.predicates.not_combust(name, orb=8.5)[source]
Planet is NOT combust.
- Parameters:
- Return type:
- Returns:
Condition that checks if planet is NOT combust
- stellium.electional.predicates.not_debilitated(name, system='traditional')[source]
Planet is NOT in detriment or fall.
- Parameters:
- Return type:
- Returns:
Condition that checks planet is NOT debilitated
- stellium.electional.predicates.not_in_house(name, houses)[source]
Object is NOT in any of the specified houses.
- stellium.electional.predicates.not_out_of_bounds(name)[source]
Object is NOT out of bounds.
- Parameters:
name (
str) – Object name- Return type:
- Returns:
Condition that checks if object is NOT out of bounds
- stellium.electional.predicates.not_retrograde(name)[source]
Planet is NOT retrograde (direct motion).
- Parameters:
name (
str) – Planet name (e.g., “Mercury”, “Venus”, “Mars”)- Return type:
- Returns:
Condition that checks if planet is NOT retrograde
- stellium.electional.predicates.not_voc(mode='traditional')[source]
Moon is NOT void of course.
- Parameters:
mode (
str) – “traditional” (Sun-Saturn) or “modern” (includes outer planets)- Return type:
- Returns:
Condition that checks Moon is NOT void of course
- stellium.electional.predicates.on_angle(name)[source]
Object is angular (in houses 1, 4, 7, or 10).
Angular houses are the most powerful positions in electional astrology.
- Parameters:
name (
str) – Object name (e.g., “Jupiter”, “Venus”, “Moon”)- Return type:
- Returns:
Condition that checks if object is in an angular house
- stellium.electional.predicates.sign_in(name, signs)[source]
Object is in one of the specified signs.
- stellium.electional.predicates.sign_not_in(name, signs)[source]
Object is NOT in any of the specified signs.
- stellium.electional.predicates.star_on_angle(star_name, angle='ASC', orb=1.0)[source]
Fixed star is conjunct a chart angle.
This is a convenience wrapper around angle_at_degree() that looks up the star’s current longitude and checks if the specified angle is within orb of it.
Note: Fixed stars move very slowly (~50 arcseconds/year due to precession), so for practical purposes within a year search, the star’s longitude is effectively constant.
- Parameters:
- Return type:
- Returns:
Condition that checks if star is conjunct angle
Example
>>> # Find moments when Regulus is rising >>> search.where(star_on_angle("Regulus", "ASC", orb=1.0))
>>> # Find moments when Spica is culminating >>> search.where(star_on_angle("Spica", "MC", orb=0.5))
Analysis (stellium.analysis)¶
Batch chart analysis and data processing.
Batch chart calculation for large-scale analysis.
BatchCalculator provides efficient calculation of many charts at once, with support for progress tracking, filtering, and generator-based processing.
- class stellium.analysis.batch.BatchCalculator(sources)[source]
Bases:
objectEfficient batch calculation of multiple charts.
Supports calculation from: - NotableRegistry (with optional filters) - List of Native objects - Any iterable of chart data
Example:
# From NotableRegistry charts = (BatchCalculator .from_registry(category="scientist", verified=True) .with_aspects() .calculate_all()) # From list of Natives charts = BatchCalculator.from_natives(natives).calculate_all() # Generator for memory efficiency for chart in BatchCalculator.from_registry().calculate(): process(chart)
- add_analyzer(analyzer)[source]
Add a chart analyzer (e.g., PatternAnalysisEngine).
- Return type:
BatchCalculator
- calculate()[source]
Calculate charts as a generator (memory efficient).
Yields charts one at a time, suitable for processing large datasets without loading all charts into memory.
- Yields:
CalculatedChart for each source
Example:
for chart in BatchCalculator.from_registry().calculate(): # Process one chart at a time print(chart.get_object("Sun").sign)
- Return type:
- calculate_all()[source]
Calculate all charts and return as a list.
Loads all charts into memory. Use calculate() generator for large datasets that don’t fit in memory.
- Return type:
- Returns:
List of all calculated charts
Example:
charts = BatchCalculator.from_registry(category="artist").calculate_all() print(f"Calculated {len(charts)} artist charts")
- count()[source]
Get the count of sources (if known).
- Return type:
- Returns:
Number of sources, or -1 if unknown (for streaming iterables)
- classmethod from_iterable(sources)[source]
Create BatchCalculator from any iterable of Native objects.
Use this for streaming data or custom data sources.
- classmethod from_natives(natives)[source]
Create BatchCalculator from a list of Native objects.
- Parameters:
natives (
list[Native]) – List of Native objects to calculate charts for- Return type:
BatchCalculator- Returns:
BatchCalculator ready to configure and run
Example:
natives = [ Native("2000-01-01 12:00", "New York, NY", name="Person 1"), Native("1990-06-15 08:30", "Los Angeles, CA", name="Person 2"), ] batch = BatchCalculator.from_natives(natives)
- classmethod from_registry(*, category=None, event_type=None, verified=None, data_quality=None, **filters)[source]
Create BatchCalculator from NotableRegistry with optional filters.
- Parameters:
- Return type:
BatchCalculator- Returns:
BatchCalculator ready to configure and run
Example:
# All verified scientists batch = BatchCalculator.from_registry( category="scientist", verified=True ) # High-quality birth data only batch = BatchCalculator.from_registry( event_type="birth", data_quality="AA" )
- with_aspects(engine=None)[source]
Enable aspect calculation with optional custom engine.
- Return type:
BatchCalculator
- with_house_systems(engines)[source]
Set the house systems to calculate.
- Return type:
BatchCalculator
- with_orbs(engine=None)[source]
Set the orb calculation engine.
- Return type:
BatchCalculator
- with_progress(callback)[source]
Set progress callback for tracking calculation progress.
The callback receives: - current: Current chart number (1-based) - total: Total number of charts (or -1 if unknown) - name: Name of current chart being calculated
- Parameters:
callback (
Callable[[int,int,str],None]) – Function to call with progress updates- Return type:
BatchCalculator
Example:
def show_progress(current, total, name): if total > 0: print(f"Calculating {current}/{total}: {name}") else: print(f"Calculating {current}: {name}") batch = BatchCalculator.from_registry().with_progress(show_progress)
DataFrame conversion utilities for chart analysis.
Provides functions to convert CalculatedChart objects to pandas DataFrames in various schemas optimized for different analysis use cases.
Requires pandas: pip install stellium[analysis]
- stellium.analysis.frames.aspects_to_dataframe(charts, include_declination=False)[source]
Convert charts to a DataFrame with one row per aspect.
This schema is best for: - Aspect frequency analysis - Aspect pattern research - Orb distribution analysis
- Parameters:
charts – Sequence of CalculatedChart objects
include_declination – Include declination aspects (parallel/contraparallel)
- Returns:
chart_id: Links to chart-level data
chart_name: Chart name
object1: First object name
object2: Second object name
aspect_name: “Conjunction”, “Square”, etc.
aspect_degree: 0, 60, 90, 120, 180, etc.
orb: Actual orb in degrees
is_applying: Applying vs separating
aspect_type: “longitude” or “declination”
- Return type:
DataFrame with columns
Example:
from stellium.analysis import BatchCalculator, aspects_to_dataframe charts = BatchCalculator.from_registry().with_aspects().calculate_all() df = aspects_to_dataframe(charts) # Most common aspects df['aspect_name'].value_counts() # Sun-Moon aspects sun_moon = df[(df['object1'] == 'Sun') & (df['object2'] == 'Moon')]
- stellium.analysis.frames.charts_to_dataframe(charts, include_patterns=True)[source]
Convert charts to a DataFrame with one row per chart.
This schema is best for: - Comparing charts across a dataset - Element/modality distribution analysis - Chart-wide pattern matching
- Parameters:
charts – Sequence of CalculatedChart objects
include_patterns – Include pattern detection columns (requires patterns in metadata)
- Returns:
chart_id: Unique identifier
name: Chart name (from metadata)
datetime_utc: UTC datetime
julian_day: Julian day number
latitude, longitude: Location coordinates
location_name: Location name
sun_longitude, sun_sign, moon_longitude, moon_sign, moon_phase
asc_longitude, asc_sign, mc_longitude, mc_sign
fire_count, earth_count, air_count, water_count
cardinal_count, fixed_count, mutable_count
sect: “day” or “night”
retrograde_count: Number of retrograde planets
has_grand_trine, has_t_square, has_grand_cross (if include_patterns)
- Return type:
DataFrame with columns
Example:
from stellium.analysis import BatchCalculator, charts_to_dataframe charts = BatchCalculator.from_registry(category="artist").calculate_all() df = charts_to_dataframe(charts) # Filter by sun sign aries_suns = df[df['sun_sign'] == 'Aries']
- stellium.analysis.frames.positions_to_dataframe(charts, object_types=None)[source]
Convert charts to a DataFrame with one row per celestial position.
This schema is best for: - Position distributions across many charts - Sign/house analysis - Speed and retrograde analysis
- Parameters:
charts – Sequence of CalculatedChart objects
object_types – Filter to specific ObjectTypes (default: all)
- Returns:
chart_id: Links to chart-level data
chart_name: Chart name
object_name: “Sun”, “Moon”, etc.
object_type: “planet”, “angle”, etc.
longitude: Ecliptic longitude (0-360)
latitude: Ecliptic latitude
sign: Zodiac sign
sign_degree: Degree within sign (0-30)
house: House placement (1-12, if available)
speed: Longitude speed (deg/day)
is_retrograde: Retrograde flag
declination: Declination (nullable)
is_out_of_bounds: OOB flag
- Return type:
DataFrame with columns
Example:
from stellium.analysis import BatchCalculator, positions_to_dataframe charts = BatchCalculator.from_registry().calculate_all() df = positions_to_dataframe(charts) # Sun sign distribution sun_df = df[df['object_name'] == 'Sun'] sun_df['sign'].value_counts()
- stellium.analysis.frames.to_aspects_dataframe(charts, include_declination=False)
Convert charts to a DataFrame with one row per aspect.
This schema is best for: - Aspect frequency analysis - Aspect pattern research - Orb distribution analysis
- Parameters:
charts – Sequence of CalculatedChart objects
include_declination – Include declination aspects (parallel/contraparallel)
- Returns:
chart_id: Links to chart-level data
chart_name: Chart name
object1: First object name
object2: Second object name
aspect_name: “Conjunction”, “Square”, etc.
aspect_degree: 0, 60, 90, 120, 180, etc.
orb: Actual orb in degrees
is_applying: Applying vs separating
aspect_type: “longitude” or “declination”
- Return type:
DataFrame with columns
Example:
from stellium.analysis import BatchCalculator, aspects_to_dataframe charts = BatchCalculator.from_registry().with_aspects().calculate_all() df = aspects_to_dataframe(charts) # Most common aspects df['aspect_name'].value_counts() # Sun-Moon aspects sun_moon = df[(df['object1'] == 'Sun') & (df['object2'] == 'Moon')]
- stellium.analysis.frames.to_chart_dataframe(charts, include_patterns=True)
Convert charts to a DataFrame with one row per chart.
This schema is best for: - Comparing charts across a dataset - Element/modality distribution analysis - Chart-wide pattern matching
- Parameters:
charts – Sequence of CalculatedChart objects
include_patterns – Include pattern detection columns (requires patterns in metadata)
- Returns:
chart_id: Unique identifier
name: Chart name (from metadata)
datetime_utc: UTC datetime
julian_day: Julian day number
latitude, longitude: Location coordinates
location_name: Location name
sun_longitude, sun_sign, moon_longitude, moon_sign, moon_phase
asc_longitude, asc_sign, mc_longitude, mc_sign
fire_count, earth_count, air_count, water_count
cardinal_count, fixed_count, mutable_count
sect: “day” or “night”
retrograde_count: Number of retrograde planets
has_grand_trine, has_t_square, has_grand_cross (if include_patterns)
- Return type:
DataFrame with columns
Example:
from stellium.analysis import BatchCalculator, charts_to_dataframe charts = BatchCalculator.from_registry(category="artist").calculate_all() df = charts_to_dataframe(charts) # Filter by sun sign aries_suns = df[df['sun_sign'] == 'Aries']
- stellium.analysis.frames.to_positions_dataframe(charts, object_types=None)
Convert charts to a DataFrame with one row per celestial position.
This schema is best for: - Position distributions across many charts - Sign/house analysis - Speed and retrograde analysis
- Parameters:
charts – Sequence of CalculatedChart objects
object_types – Filter to specific ObjectTypes (default: all)
- Returns:
chart_id: Links to chart-level data
chart_name: Chart name
object_name: “Sun”, “Moon”, etc.
object_type: “planet”, “angle”, etc.
longitude: Ecliptic longitude (0-360)
latitude: Ecliptic latitude
sign: Zodiac sign
sign_degree: Degree within sign (0-30)
house: House placement (1-12, if available)
speed: Longitude speed (deg/day)
is_retrograde: Retrograde flag
declination: Declination (nullable)
is_out_of_bounds: OOB flag
- Return type:
DataFrame with columns
Example:
from stellium.analysis import BatchCalculator, positions_to_dataframe charts = BatchCalculator.from_registry().calculate_all() df = positions_to_dataframe(charts) # Sun sign distribution sun_df = df[df['object_name'] == 'Sun'] sun_df['sign'].value_counts()
Research query interface for filtering chart collections.
ChartQuery provides a fluent API for filtering charts by astrological criteria like sun sign, moon phase, aspects, patterns, and more.
- class stellium.analysis.queries.ChartQuery(charts)[source]
Bases:
objectFluent query interface for filtering chart collections.
Supports chained method calls to build complex filters. Filters are lazily evaluated when results() or count() is called.
Example:
from stellium.analysis import ChartQuery # Find charts with Sun in Aries and Moon in Cancer matches = (ChartQuery(charts) .where_sun(sign="Aries") .where_moon(sign=["Cancer", "Scorpio"]) .where_planet("Mars", house=10) .results()) # Get as DataFrame df = ChartQuery(charts).where_sun(sign="Aries").to_dataframe() # Count results count = ChartQuery(charts).where_moon(sign="Leo").count()
- count()[source]
Execute the query and return count of matching charts.
- Return type:
- Returns:
Number of charts matching all filters
- first()[source]
Return the first matching chart, or None if no matches.
- Return type:
- Returns:
First CalculatedChart matching all filters, or None
- results()[source]
Execute the query and return matching charts.
- Return type:
- Returns:
List of CalculatedChart objects matching all filters
- to_dataframe(include_patterns=True)[source]
Execute the query and return results as a DataFrame.
Requires pandas: pip install stellium[analysis]
- where_angle(name, sign=None, degree_min=None, degree_max=None)[source]
Filter charts by angle position (ASC, MC, DSC, IC).
- Parameters:
- Return type:
ChartQuery
Example:
query.where_angle("ASC", sign="Leo") query.where_angle("MC", sign=["Aries", "Capricorn"])
- where_aspect(object1, object2, aspect=None, orb_max=None, applying=None)[source]
Filter charts by presence of specific aspects.
- Parameters:
- Return type:
ChartQuery
Example:
query.where_aspect("Sun", "Moon", aspect="conjunction") query.where_aspect("Venus", "Mars", orb_max=3.0) query.where_aspect("Saturn", "Sun", applying=True)
- where_custom(predicate)[source]
Filter charts with a custom predicate function.
- Parameters:
predicate (
Callable[[CalculatedChart],bool]) – Function that takes a CalculatedChart and returns bool- Return type:
ChartQuery
Example:
# Charts with more than 3 retrograde planets query.where_custom( lambda chart: sum(1 for p in chart.get_planets() if p.is_retrograde) > 3 )
- where_element_dominant(element, min_count=4)[source]
Filter charts where an element is dominant.
- Parameters:
- Return type:
ChartQuery
Example:
query.where_element_dominant("fire") query.where_element_dominant("water", min_count=5)
- where_modality_dominant(modality, min_count=4)[source]
Filter charts where a modality is dominant.
- Parameters:
- Return type:
ChartQuery
Example:
query.where_modality_dominant("cardinal") query.where_modality_dominant("fixed", min_count=5)
- where_moon(sign=None, house=None, degree_min=None, degree_max=None, phase=None)[source]
Filter charts by Moon position or phase.
- Parameters:
- Return type:
ChartQuery
Example:
query.where_moon(sign="Cancer") query.where_moon(phase="Full Moon") query.where_moon(phase=["New Moon", "Full Moon"])
- where_pattern(pattern_name)[source]
Filter charts by presence of aspect patterns.
- Parameters:
pattern_name (
str) – Pattern name (e.g., “Grand Trine”, “T-Square”, “Yod”)- Return type:
ChartQuery
Example:
query.where_pattern("Grand Trine") query.where_pattern("T-Square")
- where_planet(name, sign=None, house=None, degree_min=None, degree_max=None, retrograde=None, out_of_bounds=None)[source]
Filter charts by any planet’s position.
- Parameters:
- Return type:
ChartQuery
Example:
query.where_planet("Mars", sign="Aries", retrograde=True) query.where_planet("Venus", house=[5, 7]) query.where_planet("Mercury", out_of_bounds=True)
- where_sect(sect)[source]
Filter charts by sect (day or night).
- Parameters:
sect (
str) – “day” or “night”- Return type:
ChartQuery
Example:
query.where_sect("day") # Day charts only
- where_sun(sign=None, house=None, degree_min=None, degree_max=None)[source]
Filter charts by Sun position.
- Parameters:
- Return type:
ChartQuery
Example:
query.where_sun(sign="Aries") query.where_sun(sign=["Aries", "Leo", "Sagittarius"]) # Fire signs query.where_sun(house=10) query.where_sun(degree_min=0, degree_max=5) # Early degrees
Statistical aggregation for chart collections.
ChartStats provides methods for computing aggregate statistics across multiple charts, including element distributions, sign frequencies, aspect counts, and cross-tabulations.
- class stellium.analysis.stats.ChartStats(charts)[source]
Bases:
objectStatistical aggregation across chart collections.
Computes distributions, frequencies, and cross-tabulations for research and analysis purposes.
Example:
from stellium.analysis import BatchCalculator, ChartStats charts = BatchCalculator.from_registry(category="scientist").calculate_all() stats = ChartStats(charts) # Element distribution print(stats.element_distribution()) # Sun sign frequency print(stats.sign_distribution("Sun")) # Cross-tabulation print(stats.cross_tab("sun_sign", "moon_sign"))
- aspect_frequency(normalize=False)[source]
Count aspect types across all charts.
- Parameters:
normalize (
bool) – Return proportions instead of counts- Return type:
- Returns:
Dictionary with aspect names as keys and counts as values
Example:
stats.aspect_frequency() # {'Conjunction': 1234, 'Square': 987, 'Trine': 876, ...}
- aspect_pair_frequency(object1, object2)[source]
Count aspect types between two specific objects.
- Parameters:
- Return type:
- Returns:
Dictionary with aspect names as keys and counts as values
Example:
stats.aspect_pair_frequency("Sun", "Moon") # {'Conjunction': 45, 'Sextile': 38, 'Square': 42, ...}
- property chart_count: int
Number of charts in the collection.
- cross_tab(row_var, col_var)[source]
Create a cross-tabulation (contingency table) of two variables.
Requires pandas: pip install stellium[analysis]
Supported variables: - “sun_sign”, “moon_sign”, “asc_sign”, “mc_sign” - “sun_house”, “moon_house”, etc. (any object + “_house”) - “sect” (day/night) - Any object name followed by “_sign” or “_house”
- Parameters:
- Return type:
- Returns:
pandas DataFrame with cross-tabulation
Example:
# Sun sign vs Moon sign df = stats.cross_tab("sun_sign", "moon_sign") # Sun sign vs sect df = stats.cross_tab("sun_sign", "sect")
- element_distribution(normalize=True)[source]
Calculate element distribution across all charts.
Counts planets in each element across all charts and returns the distribution as proportions (default) or raw counts.
- Parameters:
normalize (
bool) – Return proportions (0-1) instead of counts- Return type:
- Returns:
Dictionary with element names as keys and counts/proportions as values
Example:
stats.element_distribution() # {'fire': 0.28, 'earth': 0.31, 'air': 0.22, 'water': 0.19} stats.element_distribution(normalize=False) # {'fire': 280, 'earth': 310, 'air': 220, 'water': 190}
- house_distribution(object_name, house_system=None, normalize=False)[source]
Count house placements for a specific object across all charts.
- Parameters:
- Return type:
- Returns:
Dictionary with house numbers (1-12) as keys and counts as values
Example:
stats.house_distribution("Sun") # {1: 35, 2: 42, 3: 38, ..., 12: 41}
- modality_distribution(normalize=True)[source]
Calculate modality distribution across all charts.
- Parameters:
normalize (
bool) – Return proportions (0-1) instead of counts- Return type:
- Returns:
Dictionary with modality names as keys and counts/proportions as values
Example:
stats.modality_distribution() # {'cardinal': 0.35, 'fixed': 0.33, 'mutable': 0.32}
- pattern_frequency()[source]
Count aspect patterns across all charts.
Example:
stats.pattern_frequency() # {'Grand Trine': 23, 'T-Square': 45, 'Yod': 12, ...}
- retrograde_frequency(normalize=False)[source]
Count retrograde occurrences by planet.
- Parameters:
normalize (
bool) – Return proportions instead of counts- Return type:
- Returns:
Dictionary with planet names as keys and retrograde counts as values
Example:
stats.retrograde_frequency() # {'Mercury': 89, 'Venus': 23, 'Mars': 45, ...}
- sect_distribution()[source]
Count day vs night charts.
Example:
stats.sect_distribution() # {'day': 523, 'night': 477}
- sign_distribution(object_name, normalize=False)[source]
Count sign placements for a specific object across all charts.
- Parameters:
- Return type:
- Returns:
Dictionary with sign names as keys and counts as values
Example:
stats.sign_distribution("Sun") # {'Aries': 45, 'Taurus': 38, 'Gemini': 42, ...} stats.sign_distribution("Moon", normalize=True) # {'Aries': 0.08, 'Taurus': 0.09, ...}
Export utilities for chart collections.
Provides functions to export charts to CSV, JSON, and other formats for external analysis tools.
- stellium.analysis.export.export_csv(charts, path, schema='charts', **kwargs)[source]
Export charts to a CSV file.
Requires pandas: pip install stellium[analysis]
- Parameters:
charts (
Sequence[CalculatedChart]) – Sequence of CalculatedChart objectsschema (
Literal['charts','positions','aspects']) – Data schema to use: - “charts”: One row per chart (default) - “positions”: One row per celestial position - “aspects”: One row per aspect**kwargs – Additional arguments passed to DataFrame.to_csv()
- Return type:
Example:
from stellium.analysis import BatchCalculator, export_csv charts = BatchCalculator.from_registry(category="artist").calculate_all() # Export chart-level data export_csv(charts, "artists.csv") # Export all positions export_csv(charts, "artists_positions.csv", schema="positions") # Export aspects export_csv(charts, "artists_aspects.csv", schema="aspects")
- stellium.analysis.export.export_json(charts, path, indent=2, lines=False)[source]
Export charts to JSON format.
- Parameters:
- Return type:
Example:
from stellium.analysis import BatchCalculator, export_json charts = BatchCalculator.from_registry(category="artist").calculate_all() # Standard JSON array export_json(charts, "artists.json") # JSON Lines (for streaming/large datasets) export_json(charts, "artists.jsonl", lines=True)
- stellium.analysis.export.export_parquet(charts, path, schema='charts')[source]
Export charts to Parquet format (columnar, efficient for big data).
Requires pandas and pyarrow: pip install stellium[analysis] pyarrow
- Parameters:
charts (
Sequence[CalculatedChart]) – Sequence of CalculatedChart objectsschema (
Literal['charts','positions','aspects']) – Data schema (same as export_csv)
- Return type:
Example:
from stellium.analysis import BatchCalculator, export_parquet charts = BatchCalculator.from_registry().calculate_all() export_parquet(charts, "all_charts.parquet")
- stellium.analysis.export.to_csv(charts, path, schema='charts', **kwargs)
Export charts to a CSV file.
Requires pandas: pip install stellium[analysis]
- Parameters:
charts (
Sequence[CalculatedChart]) – Sequence of CalculatedChart objectsschema (
Literal['charts','positions','aspects']) – Data schema to use: - “charts”: One row per chart (default) - “positions”: One row per celestial position - “aspects”: One row per aspect**kwargs – Additional arguments passed to DataFrame.to_csv()
- Return type:
Example:
from stellium.analysis import BatchCalculator, export_csv charts = BatchCalculator.from_registry(category="artist").calculate_all() # Export chart-level data export_csv(charts, "artists.csv") # Export all positions export_csv(charts, "artists_positions.csv", schema="positions") # Export aspects export_csv(charts, "artists_aspects.csv", schema="aspects")
- stellium.analysis.export.to_json(charts, path, indent=2, lines=False)
Export charts to JSON format.
- Parameters:
- Return type:
Example:
from stellium.analysis import BatchCalculator, export_json charts = BatchCalculator.from_registry(category="artist").calculate_all() # Standard JSON array export_json(charts, "artists.json") # JSON Lines (for streaming/large datasets) export_json(charts, "artists.jsonl", lines=True)
- stellium.analysis.export.to_parquet(charts, path, schema='charts')
Export charts to Parquet format (columnar, efficient for big data).
Requires pandas and pyarrow: pip install stellium[analysis] pyarrow
- Parameters:
charts (
Sequence[CalculatedChart]) – Sequence of CalculatedChart objectsschema (
Literal['charts','positions','aspects']) – Data schema (same as export_csv)
- Return type:
Example:
from stellium.analysis import BatchCalculator, export_parquet charts = BatchCalculator.from_registry().calculate_all() export_parquet(charts, "all_charts.parquet")
Generate embedding vectors to represent individual charts for fast comparison.
I/O (stellium.io)¶
Input/output formats for chart data.
Parser for AAF (Astrodienst Astrological Format) files.
AAF is the export format used by astro.com (Astrodienst). It contains birth data in a structured text format with two lines per record: - #A93: Human-readable data (name, date, time, location) - #B93: Computed data (Julian day, coordinates, timezone)
- Example AAF record:
#A93:Louie,Kate,f,6.1.1994,11:47,Mountain View (Santa Clara County),CA (US) #B93:2449359.32431,37n23,122w05,8hw00,0
- stellium.io.aaf.parse_aaf(path)[source]
Parse an AAF (Astrodienst Astrological Format) file into Native objects.
AAF is the export format from astro.com. Each chart record consists of two lines: #A93 (human-readable data) and #B93 (computed values).
- Parameters:
- Return type:
- Returns:
List of Native objects, one per chart in the file
- Raises:
FileNotFoundError – If the file doesn’t exist
ValueError – If the file format is invalid
Example
>>> natives = parse_aaf("my_charts.aaf") >>> len(natives) 20 >>> natives[0].name 'Kate Louie' >>> chart = ChartBuilder.from_native(natives[0]).calculate()
Parser for CSV files containing birth data.
CSV files are a common format for batch chart data. This module provides flexible parsing with configurable column mapping to accommodate different CSV formats and naming conventions.
Example CSV formats supported:
# Standard format (auto-detected): name,date,time,location Kate Louie,1994-01-06,11:47,Mountain View CA
# Combined datetime: name,datetime,place Kate,1994-01-06 11:47,37.3861,-122.0839
# Separate date components: first_name,last_name,year,month,day,hour,minute,latitude,longitude Kate,Louie,1994,1,6,11,47,37.3861,-122.0839
# With timezone: Name,Birth Date,Birth Time,City,Timezone Kate Louie,01/06/1994,11:47 AM,Mountain View CA,America/Los_Angeles
- class stellium.io.csv.CSVColumnMapping(name=None, datetime=None, date=None, time=None, year=None, month=None, day=None, hour=None, minute=None, second=None, location=None, latitude=None, longitude=None, timezone=None, time_unknown=None, date_format=None, time_format=None, datetime_format=None)[source]
Bases:
objectConfiguration for mapping CSV columns to Native fields.
This allows flexible handling of different CSV formats. All column names are case-insensitive and support multiple aliases.
- name
Column(s) for person/event name. Can be a single column name or a tuple for (first_name, last_name) to combine.
- datetime
Column for combined datetime string (e.g., “1994-01-06 11:47”)
- date
Column for date only (when datetime is split)
- time
Column for time only (when datetime is split)
- year
Column for year (when date is split into components)
- month
Column for month
- day
Column for day
- hour
Column for hour (when time is split into components)
- minute
Column for minute
- second
Column for second
- location
Column for location string (geocoded if no lat/lon, or used as display name if lat/lon are provided)
- latitude
Column for latitude (when using coordinates)
- longitude
Column for longitude (when using coordinates)
- timezone
Column for timezone name (e.g., “America/Los_Angeles”)
- time_unknown
Column indicating if birth time is unknown (bool/flag)
- Location handling:
If latitude + longitude are provided: Uses coordinates directly. If location is also provided, it’s used as the display name.
If only location is provided (no lat/lon): Geocodes the string.
- stellium.io.csv.parse_csv(path, mapping=None, *, delimiter=',', encoding='utf-8', skip_errors=True)[source]
Parse a CSV file containing birth data into Native objects.
This function supports flexible CSV formats through column mapping. If no mapping is provided, it will auto-detect columns based on common naming conventions.
- Parameters:
mapping (
CSVColumnMapping|None) – Optional column mapping configuration. If None, auto-detects columns from headers.delimiter (
str) – CSV delimiter character (default: comma)encoding (
str) – File encoding (default: utf-8)skip_errors (
bool) – If True, skip rows that fail to parse and continue. If False, raise an exception on the first error.
- Return type:
- Returns:
List of Native objects, one per valid row in the CSV
- Raises:
FileNotFoundError – If the file doesn’t exist
ValueError – If required columns are missing or skip_errors=False and a row fails to parse
Example
# Auto-detect columns >>> natives = parse_csv(“birth_data.csv”)
# Custom column mapping >>> mapping = CSVColumnMapping( … name=”Full Name”, … date=”DOB”, … time=”Birth Time”, … location=”Birth Place”, … ) >>> natives = parse_csv(“birth_data.csv”, mapping=mapping)
# With date format hint for ambiguous dates >>> mapping = CSVColumnMapping( … date=”date”, … date_format=”%d/%m/%Y”, # European format … ) >>> natives = parse_csv(“european_data.csv”, mapping=mapping)
- stellium.io.csv.read_csv(path, *, name=None, datetime=None, date=None, time=None, location=None, latitude=None, longitude=None, date_format=None, time_format=None)[source]
Simple interface for reading CSV files with common column configurations.
This is a convenience wrapper around parse_csv() that allows specifying column names as keyword arguments.
- Parameters:
- Return type:
- Returns:
List of Native objects
Example
# Simple auto-detection >>> natives = read_csv(“data.csv”)
# Specify key columns >>> natives = read_csv( … “data.csv”, … name=”Full Name”, … date=”DOB”, … time=”Birth Time”, … location=”City”, … )
# Combined first/last name >>> natives = read_csv( … “data.csv”, … name=(“First Name”, “Last Name”), … datetime=”Birth DateTime”, … latitude=”Lat”, … longitude=”Long”, … )
Parser for pandas DataFrames containing birth data.
This module provides the same flexible parsing as the CSV module, but works directly with in-memory pandas DataFrames. This is useful when data comes from databases, Excel files, or other pandas-compatible sources.
- Example usage:
>>> import pandas as pd >>> from stellium.io import parse_dataframe, read_dataframe >>> >>> # Load data from any source >>> df = pd.read_excel("birth_data.xlsx") >>> # Or: df = pd.read_sql("SELECT * FROM births", connection) >>> # Or: df = pd.read_parquet("data.parquet") >>> >>> # Auto-detect columns >>> natives = parse_dataframe(df) >>> >>> # Or specify columns explicitly >>> natives = read_dataframe( ... df, ... name="Full Name", ... date="DOB", ... time="Birth Time", ... latitude="Lat", ... longitude="Long", ... )
- stellium.io.dataframe.dataframe_from_natives(natives, *, include_coords=True, include_timezone=False)[source]
Convert a list of Native objects back to a pandas DataFrame.
This is useful for exporting processed data or for round-trip operations.
- Parameters:
natives (list[Native]) – List of Native objects to convert
include_coords (bool) – Include latitude/longitude columns (default: True)
include_timezone (bool) – Include timezone column (default: False)
- Return type:
pd.DataFrame
- Returns:
pandas DataFrame with birth data
Example
>>> from stellium.io import parse_csv, dataframe_from_natives >>> >>> natives = parse_csv("birth_data.csv") >>> df = dataframe_from_natives(natives) >>> df.to_excel("birth_data.xlsx") # Export to Excel
- stellium.io.dataframe.parse_dataframe(df, mapping=None, *, skip_errors=True)[source]
Parse a pandas DataFrame containing birth data into Native objects.
This function supports flexible DataFrame formats through column mapping. If no mapping is provided, it will auto-detect columns based on common naming conventions.
- Parameters:
df (pd.DataFrame) – pandas DataFrame with birth data
mapping (CSVColumnMapping | None) – Optional column mapping configuration. If None, auto-detects columns from DataFrame column names.
skip_errors (bool) – If True, skip rows that fail to parse and continue. If False, raise an exception on the first error.
- Return type:
list[Native]
- Returns:
List of Native objects, one per valid row in the DataFrame
- Raises:
ImportError – If pandas is not installed
ValueError – If required columns are missing or skip_errors=False and a row fails to parse
Example
>>> import pandas as pd >>> from stellium.io import parse_dataframe >>> >>> df = pd.DataFrame({ ... "name": ["Kate Louie", "Albert Einstein"], ... "date": ["1994-01-06", "1879-03-14"], ... "time": ["11:47", "11:30"], ... "latitude": [37.3861, 48.4011], ... "longitude": [-122.0839, 9.9876], ... }) >>> natives = parse_dataframe(df) >>> len(natives) 2
>>> # With custom column mapping >>> mapping = CSVColumnMapping( ... name="Full Name", ... date="DOB", ... latitude="Lat", ... longitude="Lon", ... ) >>> natives = parse_dataframe(df, mapping=mapping)
- stellium.io.dataframe.read_dataframe(df, *, name=None, datetime=None, date=None, time=None, location=None, latitude=None, longitude=None, date_format=None, time_format=None)[source]
Simple interface for reading pandas DataFrames with common column configurations.
This is a convenience wrapper around parse_dataframe() that allows specifying column names as keyword arguments.
- Parameters:
df (pd.DataFrame) – pandas DataFrame with birth data
name (str | tuple[str, str] | None) – Column name for person/event name, or tuple of (first, last)
datetime (str | None) – Column name for combined datetime
date (str | None) – Column name for date
time (str | None) – Column name for time
location (str | None) – Column name for location string
latitude (str | None) – Column name for latitude
longitude (str | None) – Column name for longitude
date_format (str | None) – strptime format for dates (e.g., “%d/%m/%Y”)
time_format (str | None) – strptime format for times (e.g., “%I:%M %p”)
- Return type:
list[Native]
- Returns:
List of Native objects
Example
>>> import pandas as pd >>> from stellium.io import read_dataframe >>> >>> df = pd.DataFrame({ ... "Person": ["Kate Louie"], ... "Birthday": ["1994-01-06"], ... "Birth Time": ["11:47"], ... "Lat": [37.3861], ... "Long": [-122.0839], ... }) >>> >>> natives = read_dataframe( ... df, ... name="Person", ... date="Birthday", ... time="Birth Time", ... latitude="Lat", ... longitude="Long", ... )
Chinese Astrology (stellium.chinese)¶
Ba Zi (Four Pillars) and related Chinese astrological systems.
Chinese astrology systems for Stellium.
This module provides implementations of various Chinese astrology systems: - Bazi (Four Pillars / 八字) - implemented - Zi Wei Dou Shu (Purple Star / 紫微斗數) - planned
Core primitives (stems, branches, elements) are shared across all systems. All chart types implement the ChineseChart protocol for interoperability.
Example
>>> from stellium.chinese import BaZiEngine
>>> from datetime import datetime
>>>
>>> engine = BaZiEngine(timezone_offset_hours=8) # Beijing time
>>> chart = engine.calculate(datetime(1990, 5, 15, 10, 30))
>>> print(chart.display())
- stellium.chinese.BaZiCalculator
alias of
BaZiEngine
- class stellium.chinese.BaZiChart(year, month, day, hour, birth_datetime)[source]
Bases:
objectA complete Four Pillars (Bazi / 八字) chart.
The Day Stem represents the “Day Master” (日主), which is the self.
Implements the ChineseChart protocol.
- property all_branches: tuple[EarthlyBranch, ...]
All four earthly branches.
- property all_hidden_stems: list[HeavenlyStem]
All hidden stems across all four pillars.
- property all_stems: tuple[HeavenlyStem, ...]
All four heavenly stems.
- birth_datetime: datetime
- day: Pillar
- property day_master: HeavenlyStem
The Day Master (日主) - the stem that represents the self.
- property day_master_element: Element
The element of the Day Master.
- display_detailed()[source]
Detailed prose display including hidden stems and Ten Gods.
- Return type:
- element_counts(include_hidden=False)[source]
Count occurrences of each element across stems and branches.
- Parameters:
include_hidden (
bool) – If True, includes hidden stems in the count. Hidden stems are weighted: main=1.0, middle=0.5, residual=0.3- Return type:
Note: For weighted hidden stem analysis, use element_strength() instead.
- property hanzi: str
The eight characters (八字) in Chinese.
- hour: Pillar
- month: Pillar
- property pillars: tuple[Pillar, Pillar, Pillar, Pillar]
year, month, day, hour.
- Type:
All four pillars in order
- polarity_counts()[source]
Count Yin vs Yang across all stems and branches.
- strength()[source]
Analyze the Day Master’s strength.
Returns a StrengthAnalysis with classification (Strong/Weak/etc.), component scores, and favorable/unfavorable elements.
Example:
bazi = ChartBuilder.from_details("1994-01-06 11:47", "Palo Alto, CA").bazi() result = bazi.strength() print(result.strength.english) # "Strong", "Weak", etc. print(result.favorable_elements)
- Return type:
StrengthAnalysis
- property system_name: str
The name of this system.
- ten_gods(include_hidden=True)[source]
Analyze Ten Gods (十神) relationships in the chart.
- Parameters:
include_hidden (
bool) – Whether to include hidden stems in analysis- Returns:
List of TenGodRelation objects
- to_dict()[source]
Export chart data as a dictionary (JSON-serializable).
- year: Pillar
- class stellium.chinese.BaZiEngine(timezone_offset_hours=0.0)[source]
Bases:
objectCalculate Bazi (Four Pillars) charts.
Implements the ChineseChartEngine protocol.
Example
>>> from datetime import datetime >>> engine = BaZiEngine(timezone_offset_hours=-8) # PST >>> chart = engine.calculate(datetime(1994, 1, 6, 11, 47)) >>> print(chart.display())
- calculate(birth_datetime)[source]
Calculate the Four Pillars chart for a birth datetime.
- Parameters:
birth_datetime (
datetime) – The birth date and time. Should be in local time if timezone_offset was provided, otherwise UTC.- Return type:
BaZiChart- Returns:
Complete BaZiChart with all four pillars.
- property system_name: str
The name of the system this engine calculates.
- class stellium.chinese.ChineseChart(*args, **kwargs)[source]
Bases:
ProtocolProtocol for all Chinese astrology chart types.
This defines the minimum interface that Bazi, Zi Wei, Qi Men, etc. should all implement, enabling shared visualization and export logic.
- property birth_datetime: datetime
The birth/event datetime used to calculate this chart.
- property system_name: str
The name of the system (e.g., ‘Bazi’, ‘Zi Wei Dou Shu’).
- class stellium.chinese.ChineseChartEngine(*args, **kwargs)[source]
Bases:
ProtocolProtocol for Chinese chart calculation engines.
Mirrors the pattern from Western astrology engines.
- calculate(birth_datetime)[source]
Calculate a chart for the given datetime.
- Return type:
ChineseChart
- property system_name: str
The name of the system this engine calculates.
- class stellium.chinese.ChineseChartRenderer(*args, **kwargs)[source]
Bases:
ProtocolProtocol for rendering Chinese charts to visual formats.
Each system (Bazi, Zi Wei, etc.) will have its own renderer due to their vastly different visual structures.
- class stellium.chinese.EarthlyBranch(value)[source]
Bases:
EnumThe Twelve Earthly Branches (Di Zhi / 地支).
- CHEN = BranchMeta(index=4, hanzi='辰', pinyin='Chén', jyutping='San4', animal='Dragon', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('WU', 'YI', 'GUI'))
- CHOU = BranchMeta(index=1, hanzi='丑', pinyin='Chǒu', jyutping='Cau2', animal='Ox', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('JI', 'GUI', 'XIN'))
- HAI = BranchMeta(index=11, hanzi='亥', pinyin='Hài', jyutping='Hoi6', animal='Pig', element=<Element.WATER: ('Water', '水', '#2196f3')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('REN', 'JIA'))
- MAO = BranchMeta(index=3, hanzi='卯', pinyin='Mǎo', jyutping='Maau5', animal='Rabbit', element=<Element.WOOD: ('Wood', '木', '#4caf50')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('YI',))
- SHEN = BranchMeta(index=8, hanzi='申', pinyin='Shēn', jyutping='San1', animal='Monkey', element=<Element.METAL: ('Metal', '金', '#9e9e9e')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('GENG', 'REN', 'WU'))
- SI = BranchMeta(index=5, hanzi='巳', pinyin='Sì', jyutping='Zi6', animal='Snake', element=<Element.FIRE: ('Fire', '火', '#f44336')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('BING', 'WU', 'GENG'))
- WEI = BranchMeta(index=7, hanzi='未', pinyin='Wèi', jyutping='Mei6', animal='Goat', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('JI', 'DING', 'YI'))
- WU_BRANCH = BranchMeta(index=6, hanzi='午', pinyin='Wǔ', jyutping='Ng5', animal='Horse', element=<Element.FIRE: ('Fire', '火', '#f44336')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('DING', 'JI'))
- XU = BranchMeta(index=10, hanzi='戌', pinyin='Xū', jyutping='Seot1', animal='Dog', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('WU', 'XIN', 'DING'))
- YIN = BranchMeta(index=2, hanzi='寅', pinyin='Yín', jyutping='Jan4', animal='Tiger', element=<Element.WOOD: ('Wood', '木', '#4caf50')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('JIA', 'BING', 'WU'))
- YOU = BranchMeta(index=9, hanzi='酉', pinyin='Yǒu', jyutping='Jau5', animal='Rooster', element=<Element.METAL: ('Metal', '金', '#9e9e9e')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('XIN',))
- ZI = BranchMeta(index=0, hanzi='子', pinyin='Zǐ', jyutping='Zi2', animal='Rat', element=<Element.WATER: ('Water', '水', '#2196f3')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('GUI',))
- property animal: str
- property display: str
Human-readable display string.
- property element: Element
- classmethod from_index(index)[source]
Get branch by 0-11 index (modulo safe for cyclic calculations).
- Return type:
EarthlyBranch
- get_hidden_stem_objects()[source]
Get the actual HeavenlyStem objects for this branch’s hidden stems.
- Return type:
list[HeavenlyStem]
- property hanzi: str
- property index: int
- property jyutping: str
- property pinyin: str
- property polarity: Polarity
- class stellium.chinese.Element(english, hanzi, color_hex)[source]
Bases:
EnumThe Five Elements (Wu Xing / 五行).
- EARTH = ('Earth', '土', '#795548')
- FIRE = ('Fire', '火', '#f44336')
- METAL = ('Metal', '金', '#9e9e9e')
- WATER = ('Water', '水', '#2196f3')
- WOOD = ('Wood', '木', '#4caf50')
- property controlled_by: Element
What controls this element in the controlling cycle (克我).
- property controls: Element
What this element controls/overcomes in the controlling cycle (克).
- property produced_by: Element
What produces this element in the generative cycle (生我).
- property produces: Element
What this element produces in the generative cycle (生).
- class stellium.chinese.HeavenlyStem(value)[source]
Bases:
EnumThe Ten Heavenly Stems (Tian Gan / 天干).
- BING = StemMeta(index=2, hanzi='丙', pinyin='Bǐng', jyutping='Bing2', element=<Element.FIRE: ('Fire', '火', '#f44336')>, polarity=<Polarity.YANG: 'Yang'>)
- DING = StemMeta(index=3, hanzi='丁', pinyin='Dīng', jyutping='Ding1', element=<Element.FIRE: ('Fire', '火', '#f44336')>, polarity=<Polarity.YIN: 'Yin'>)
- GENG = StemMeta(index=6, hanzi='庚', pinyin='Gēng', jyutping='Gang1', element=<Element.METAL: ('Metal', '金', '#9e9e9e')>, polarity=<Polarity.YANG: 'Yang'>)
- GUI = StemMeta(index=9, hanzi='癸', pinyin='Guǐ', jyutping='Gwai3', element=<Element.WATER: ('Water', '水', '#2196f3')>, polarity=<Polarity.YIN: 'Yin'>)
- JI = StemMeta(index=5, hanzi='己', pinyin='Jǐ', jyutping='Gei2', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YIN: 'Yin'>)
- JIA = StemMeta(index=0, hanzi='甲', pinyin='Jiǎ', jyutping='Gaap3', element=<Element.WOOD: ('Wood', '木', '#4caf50')>, polarity=<Polarity.YANG: 'Yang'>)
- REN = StemMeta(index=8, hanzi='壬', pinyin='Rén', jyutping='Jam4', element=<Element.WATER: ('Water', '水', '#2196f3')>, polarity=<Polarity.YANG: 'Yang'>)
- WU = StemMeta(index=4, hanzi='戊', pinyin='Wù', jyutping='Mou6', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YANG: 'Yang'>)
- XIN = StemMeta(index=7, hanzi='辛', pinyin='Xīn', jyutping='San1', element=<Element.METAL: ('Metal', '金', '#9e9e9e')>, polarity=<Polarity.YIN: 'Yin'>)
- YI = StemMeta(index=1, hanzi='乙', pinyin='Yǐ', jyutping='Jyut6', element=<Element.WOOD: ('Wood', '木', '#4caf50')>, polarity=<Polarity.YIN: 'Yin'>)
- property display: str
Human-readable display string.
- property display_canto: str
Display string with Cantonese romanization.
- property element: Element
- classmethod from_index(index)[source]
Get stem by 0-9 index (modulo safe for cyclic calculations).
- Return type:
HeavenlyStem
- property hanzi: str
- property index: int
- property jyutping: str
- property pinyin: str
- property polarity: Polarity
- class stellium.chinese.Pillar(stem, branch)[source]
Bases:
objectA single pillar (柱) consisting of a Stem and Branch.
- property animal: str
The zodiac animal of the branch.
- branch: EarthlyBranch
- property branch_element: Element
The element of the branch.
- property hanzi: str
The two-character Chinese representation.
- property hidden_stems: list[HeavenlyStem]
The hidden stems (藏干) within the branch.
- property pinyin: str
Pinyin romanization.
- stem: HeavenlyStem
- property stem_element: Element
The element of the stem (primary element of the pillar).
- class stellium.chinese.Polarity(value)[source]
Bases:
EnumYin and Yang polarities.
- YANG = 'Yang'
- YIN = 'Yin'
- property hanzi: str
- class stellium.chinese.SolarTerm(index, longitude, hanzi, english)[source]
Bases:
EnumThe 24 Solar Terms (Jie Qi / 节气).
Values are (index, solar_longitude, chinese_name, english_name). Index 0 = Li Chun (Start of Spring) at 315°.
- BAI_LU = (14, 165, '白露', 'White Dew')
- CHUN_FEN = (3, 0, '春分', 'Spring Equinox')
- CHU_SHU = (13, 150, '处暑', 'End of Heat')
- DA_HAN = (23, 300, '大寒', 'Major Cold')
- DA_SHU = (11, 120, '大暑', 'Major Heat')
- DA_XUE = (20, 255, '大雪', 'Major Snow')
- DONG_ZHI = (21, 270, '冬至', 'Winter Solstice')
- GU_YU = (5, 30, '谷雨', 'Grain Rain')
- HAN_LU = (16, 195, '寒露', 'Cold Dew')
- JING_ZHE = (2, 345, '惊蛰', 'Awakening of Insects')
- LI_CHUN = (0, 315, '立春', 'Start of Spring')
- LI_DONG = (18, 225, '立冬', 'Start of Winter')
- LI_QIU = (12, 135, '立秋', 'Start of Autumn')
- LI_XIA = (6, 45, '立夏', 'Start of Summer')
- MANG_ZHONG = (8, 75, '芒种', 'Grain in Ear')
- QING_MING = (4, 15, '清明', 'Clear and Bright')
- QIU_FEN = (15, 180, '秋分', 'Autumn Equinox')
- SHUANG_JIANG = (17, 210, '霜降', "Frost's Descent")
- XIAO_HAN = (22, 285, '小寒', 'Minor Cold')
- XIAO_MAN = (7, 60, '小满', 'Grain Buds')
- XIAO_SHU = (10, 105, '小暑', 'Minor Heat')
- XIAO_XUE = (19, 240, '小雪', 'Minor Snow')
- XIA_ZHI = (9, 90, '夏至', 'Summer Solstice')
- YU_SHUI = (1, 330, '雨水', 'Rain Water')
- classmethod from_longitude(longitude)[source]
Get the solar term for a given solar longitude.
- Return type:
SolarTerm
- property is_major_term: bool
Major terms (Jie) mark Bazi month boundaries. They’re the odd-indexed terms.
- class stellium.chinese.SolarTermEngine[source]
Bases:
objectCalculates solar terms (Jie Qi) for Chinese calendar systems.
- LI_CHUN_DEGREE = 315.0
- classmethod find_month_start(jd)[source]
Find the exact moment the current Bazi month started.
Returns the SolarTermEvent for the Jie (major term) that began this month.
- Return type:
SolarTermEvent
- classmethod find_term_crossing(target_longitude, jd_start, direction='forward')[source]
Find the exact Julian Day when the Sun crosses a specific longitude.
Uses the existing search engine with hybrid Newton-Raphson / bisection.
- classmethod get_bazi_month_index(jd)[source]
Get the Bazi month index (0-11) for a given Julian Day.
Month 0 = Tiger month (starts at Li Chun, 315°) Month 11 = Ox month (starts at Xiao Han, 285°)
- Return type:
- classmethod get_current_term(jd)[source]
Get the solar term that’s currently active at this Julian Day.
- Return type:
SolarTerm
- class stellium.chinese.SolarTermEvent(term, datetime_utc, julian_day)[source]
Bases:
objectA solar term occurrence at a specific moment in time.
- datetime_utc: datetime
- julian_day: float
- term: SolarTerm
- class stellium.chinese.TenGod(english, chinese, hanzi, short_code)[source]
Bases:
EnumThe Ten Gods (十神) relationship types.
Each value contains: (english_name, chinese_name, hanzi, short_code)
- BI_JIAN = ('Friend', '比肩', '比', 'BJ')
- JIE_CAI = ('Rob Wealth', '劫财', '劫', 'JC')
- PIAN_CAI = ('Indirect Wealth', '偏财', '偏财', 'PC')
- PIAN_YIN = ('Indirect Seal', '偏印', '枭', 'PY')
- QI_SHA = ('Seven Killings', '七杀', '杀', 'QS')
- SELF = ('Self', '日主', '我', 'DM')
- SHANG_GUAN = ('Hurting Officer', '伤官', '伤', 'SG')
- SHI_SHEN = ('Eating God', '食神', '食', 'SS')
- ZHENG_CAI = ('Direct Wealth', '正财', '正财', 'ZC')
- ZHENG_GUAN = ('Direct Officer', '正官', '官', 'ZG')
- ZHENG_YIN = ('Direct Seal', '正印', '印', 'ZY')
- property category: str
The category this god belongs to.
- property is_direct: bool
Whether this is a ‘direct/正’ relationship (different polarities).
- class stellium.chinese.TenGodRelation(stem, ten_god, pillar_name, is_hidden=False)[source]
Bases:
objectA Ten God relationship for a specific stem in the chart.
- property display: str
Human-readable display.
- is_hidden: bool = False
- pillar_name: str
- stem: HeavenlyStem
- ten_god: TenGod
- stellium.chinese.analyze_ten_gods(chart, include_hidden=True)[source]
Analyze all Ten God relationships in a Bazi chart.
- stellium.chinese.calculate_ten_god(day_master, other_stem)[source]
Calculate the Ten God relationship between Day Master and another stem.
- Parameters:
day_master (
HeavenlyStem) – The Day Master (日主) stemother_stem (
HeavenlyStem) – The stem to analyze
- Return type:
TenGod- Returns:
The TenGod relationship
Core primitives for Chinese astrology systems.
This module contains the fundamental building blocks shared across different Chinese astrology systems (Bazi, Zi Wei Dou Shu, etc.): - Polarity (Yin/Yang) - Five Elements (Wu Xing) - Ten Heavenly Stems (Tian Gan) - Twelve Earthly Branches (Di Zhi)
- class stellium.chinese.core.BranchMeta(index, hanzi, pinyin, jyutping, animal, element, polarity, hidden_stems=())[source]
Bases:
objectMetadata for an Earthly Branch.
- animal: str
- element: Element
- hanzi: str
- index: int
- jyutping: str
- pinyin: str
- polarity: Polarity
- class stellium.chinese.core.EarthlyBranch(value)[source]
Bases:
EnumThe Twelve Earthly Branches (Di Zhi / 地支).
- CHEN = BranchMeta(index=4, hanzi='辰', pinyin='Chén', jyutping='San4', animal='Dragon', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('WU', 'YI', 'GUI'))
- CHOU = BranchMeta(index=1, hanzi='丑', pinyin='Chǒu', jyutping='Cau2', animal='Ox', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('JI', 'GUI', 'XIN'))
- HAI = BranchMeta(index=11, hanzi='亥', pinyin='Hài', jyutping='Hoi6', animal='Pig', element=<Element.WATER: ('Water', '水', '#2196f3')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('REN', 'JIA'))
- MAO = BranchMeta(index=3, hanzi='卯', pinyin='Mǎo', jyutping='Maau5', animal='Rabbit', element=<Element.WOOD: ('Wood', '木', '#4caf50')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('YI',))
- SHEN = BranchMeta(index=8, hanzi='申', pinyin='Shēn', jyutping='San1', animal='Monkey', element=<Element.METAL: ('Metal', '金', '#9e9e9e')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('GENG', 'REN', 'WU'))
- SI = BranchMeta(index=5, hanzi='巳', pinyin='Sì', jyutping='Zi6', animal='Snake', element=<Element.FIRE: ('Fire', '火', '#f44336')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('BING', 'WU', 'GENG'))
- WEI = BranchMeta(index=7, hanzi='未', pinyin='Wèi', jyutping='Mei6', animal='Goat', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('JI', 'DING', 'YI'))
- WU_BRANCH = BranchMeta(index=6, hanzi='午', pinyin='Wǔ', jyutping='Ng5', animal='Horse', element=<Element.FIRE: ('Fire', '火', '#f44336')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('DING', 'JI'))
- XU = BranchMeta(index=10, hanzi='戌', pinyin='Xū', jyutping='Seot1', animal='Dog', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('WU', 'XIN', 'DING'))
- YIN = BranchMeta(index=2, hanzi='寅', pinyin='Yín', jyutping='Jan4', animal='Tiger', element=<Element.WOOD: ('Wood', '木', '#4caf50')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('JIA', 'BING', 'WU'))
- YOU = BranchMeta(index=9, hanzi='酉', pinyin='Yǒu', jyutping='Jau5', animal='Rooster', element=<Element.METAL: ('Metal', '金', '#9e9e9e')>, polarity=<Polarity.YIN: 'Yin'>, hidden_stems=('XIN',))
- ZI = BranchMeta(index=0, hanzi='子', pinyin='Zǐ', jyutping='Zi2', animal='Rat', element=<Element.WATER: ('Water', '水', '#2196f3')>, polarity=<Polarity.YANG: 'Yang'>, hidden_stems=('GUI',))
- property animal: str
- property display: str
Human-readable display string.
- property element: Element
- classmethod from_index(index)[source]
Get branch by 0-11 index (modulo safe for cyclic calculations).
- Return type:
EarthlyBranch
- get_hidden_stem_objects()[source]
Get the actual HeavenlyStem objects for this branch’s hidden stems.
- Return type:
list[HeavenlyStem]
- property hanzi: str
- property index: int
- property jyutping: str
- property pinyin: str
- property polarity: Polarity
- class stellium.chinese.core.Element(english, hanzi, color_hex)[source]
Bases:
EnumThe Five Elements (Wu Xing / 五行).
- EARTH = ('Earth', '土', '#795548')
- FIRE = ('Fire', '火', '#f44336')
- METAL = ('Metal', '金', '#9e9e9e')
- WATER = ('Water', '水', '#2196f3')
- WOOD = ('Wood', '木', '#4caf50')
- property controlled_by: Element
What controls this element in the controlling cycle (克我).
- property controls: Element
What this element controls/overcomes in the controlling cycle (克).
- property produced_by: Element
What produces this element in the generative cycle (生我).
- property produces: Element
What this element produces in the generative cycle (生).
- class stellium.chinese.core.HeavenlyStem(value)[source]
Bases:
EnumThe Ten Heavenly Stems (Tian Gan / 天干).
- BING = StemMeta(index=2, hanzi='丙', pinyin='Bǐng', jyutping='Bing2', element=<Element.FIRE: ('Fire', '火', '#f44336')>, polarity=<Polarity.YANG: 'Yang'>)
- DING = StemMeta(index=3, hanzi='丁', pinyin='Dīng', jyutping='Ding1', element=<Element.FIRE: ('Fire', '火', '#f44336')>, polarity=<Polarity.YIN: 'Yin'>)
- GENG = StemMeta(index=6, hanzi='庚', pinyin='Gēng', jyutping='Gang1', element=<Element.METAL: ('Metal', '金', '#9e9e9e')>, polarity=<Polarity.YANG: 'Yang'>)
- GUI = StemMeta(index=9, hanzi='癸', pinyin='Guǐ', jyutping='Gwai3', element=<Element.WATER: ('Water', '水', '#2196f3')>, polarity=<Polarity.YIN: 'Yin'>)
- JI = StemMeta(index=5, hanzi='己', pinyin='Jǐ', jyutping='Gei2', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YIN: 'Yin'>)
- JIA = StemMeta(index=0, hanzi='甲', pinyin='Jiǎ', jyutping='Gaap3', element=<Element.WOOD: ('Wood', '木', '#4caf50')>, polarity=<Polarity.YANG: 'Yang'>)
- REN = StemMeta(index=8, hanzi='壬', pinyin='Rén', jyutping='Jam4', element=<Element.WATER: ('Water', '水', '#2196f3')>, polarity=<Polarity.YANG: 'Yang'>)
- WU = StemMeta(index=4, hanzi='戊', pinyin='Wù', jyutping='Mou6', element=<Element.EARTH: ('Earth', '土', '#795548')>, polarity=<Polarity.YANG: 'Yang'>)
- XIN = StemMeta(index=7, hanzi='辛', pinyin='Xīn', jyutping='San1', element=<Element.METAL: ('Metal', '金', '#9e9e9e')>, polarity=<Polarity.YIN: 'Yin'>)
- YI = StemMeta(index=1, hanzi='乙', pinyin='Yǐ', jyutping='Jyut6', element=<Element.WOOD: ('Wood', '木', '#4caf50')>, polarity=<Polarity.YIN: 'Yin'>)
- property display: str
Human-readable display string.
- property display_canto: str
Display string with Cantonese romanization.
- property element: Element
- classmethod from_index(index)[source]
Get stem by 0-9 index (modulo safe for cyclic calculations).
- Return type:
HeavenlyStem
- property hanzi: str
- property index: int
- property jyutping: str
- property pinyin: str
- property polarity: Polarity
- class stellium.chinese.core.Polarity(value)[source]
Bases:
EnumYin and Yang polarities.
- YANG = 'Yang'
- YIN = 'Yin'
- property hanzi: str
- class stellium.chinese.core.StemMeta(index, hanzi, pinyin, jyutping, element, polarity)[source]
Bases:
objectMetadata for a Heavenly Stem.
- element: Element
- hanzi: str
- index: int
- jyutping: str
- pinyin: str
- polarity: Polarity
Chinese calendar calculations: solar terms, lunar calendar conversions.
This module handles the astronomical calculations needed for Chinese astrology: - Solar terms (Jie Qi / 节气) for Bazi month determination - Lunar calendar conversions (future)
Solar terms are based on the Sun’s ecliptic longitude: - Li Chun (立春, Start of Spring) = 315° - Each of the 24 terms is 15° apart - The 12 “major” terms (Zhong Qi) mark Bazi month boundaries
- class stellium.chinese.calendar.SolarTerm(index, longitude, hanzi, english)[source]
Bases:
EnumThe 24 Solar Terms (Jie Qi / 节气).
Values are (index, solar_longitude, chinese_name, english_name). Index 0 = Li Chun (Start of Spring) at 315°.
- BAI_LU = (14, 165, '白露', 'White Dew')
- CHUN_FEN = (3, 0, '春分', 'Spring Equinox')
- CHU_SHU = (13, 150, '处暑', 'End of Heat')
- DA_HAN = (23, 300, '大寒', 'Major Cold')
- DA_SHU = (11, 120, '大暑', 'Major Heat')
- DA_XUE = (20, 255, '大雪', 'Major Snow')
- DONG_ZHI = (21, 270, '冬至', 'Winter Solstice')
- GU_YU = (5, 30, '谷雨', 'Grain Rain')
- HAN_LU = (16, 195, '寒露', 'Cold Dew')
- JING_ZHE = (2, 345, '惊蛰', 'Awakening of Insects')
- LI_CHUN = (0, 315, '立春', 'Start of Spring')
- LI_DONG = (18, 225, '立冬', 'Start of Winter')
- LI_QIU = (12, 135, '立秋', 'Start of Autumn')
- LI_XIA = (6, 45, '立夏', 'Start of Summer')
- MANG_ZHONG = (8, 75, '芒种', 'Grain in Ear')
- QING_MING = (4, 15, '清明', 'Clear and Bright')
- QIU_FEN = (15, 180, '秋分', 'Autumn Equinox')
- SHUANG_JIANG = (17, 210, '霜降', "Frost's Descent")
- XIAO_HAN = (22, 285, '小寒', 'Minor Cold')
- XIAO_MAN = (7, 60, '小满', 'Grain Buds')
- XIAO_SHU = (10, 105, '小暑', 'Minor Heat')
- XIAO_XUE = (19, 240, '小雪', 'Minor Snow')
- XIA_ZHI = (9, 90, '夏至', 'Summer Solstice')
- YU_SHUI = (1, 330, '雨水', 'Rain Water')
- classmethod from_longitude(longitude)[source]
Get the solar term for a given solar longitude.
- Return type:
SolarTerm
- property is_major_term: bool
Major terms (Jie) mark Bazi month boundaries. They’re the odd-indexed terms.
- class stellium.chinese.calendar.SolarTermEngine[source]
Bases:
objectCalculates solar terms (Jie Qi) for Chinese calendar systems.
- LI_CHUN_DEGREE = 315.0
- classmethod find_month_start(jd)[source]
Find the exact moment the current Bazi month started.
Returns the SolarTermEvent for the Jie (major term) that began this month.
- Return type:
SolarTermEvent
- classmethod find_term_crossing(target_longitude, jd_start, direction='forward')[source]
Find the exact Julian Day when the Sun crosses a specific longitude.
Uses the existing search engine with hybrid Newton-Raphson / bisection.
- classmethod get_bazi_month_index(jd)[source]
Get the Bazi month index (0-11) for a given Julian Day.
Month 0 = Tiger month (starts at Li Chun, 315°) Month 11 = Ox month (starts at Xiao Han, 285°)
- Return type:
- classmethod get_current_term(jd)[source]
Get the solar term that’s currently active at this Julian Day.
- Return type:
SolarTerm
- class stellium.chinese.calendar.SolarTermEvent(term, datetime_utc, julian_day)[source]
Bases:
objectA solar term occurrence at a specific moment in time.
- datetime_utc: datetime
- julian_day: float
- term: SolarTerm
Protocols (interfaces) for Chinese astrology systems.
These define the common operations that all Chinese chart types should support, enabling polymorphic handling while allowing each system its unique structure.
- class stellium.chinese.protocols.ChineseChart(*args, **kwargs)[source]
Bases:
ProtocolProtocol for all Chinese astrology chart types.
This defines the minimum interface that Bazi, Zi Wei, Qi Men, etc. should all implement, enabling shared visualization and export logic.
- property birth_datetime: datetime
The birth/event datetime used to calculate this chart.
- property system_name: str
The name of the system (e.g., ‘Bazi’, ‘Zi Wei Dou Shu’).
- class stellium.chinese.protocols.ChineseChartEngine(*args, **kwargs)[source]
Bases:
ProtocolProtocol for Chinese chart calculation engines.
Mirrors the pattern from Western astrology engines.
- calculate(birth_datetime)[source]
Calculate a chart for the given datetime.
- Return type:
ChineseChart
- property system_name: str
The name of the system this engine calculates.
- class stellium.chinese.protocols.ChineseChartRenderer(*args, **kwargs)[source]
Bases:
ProtocolProtocol for rendering Chinese charts to visual formats.
Each system (Bazi, Zi Wei, etc.) will have its own renderer due to their vastly different visual structures.
Ba Zi (stellium.chinese.bazi)¶
Four Pillars of Destiny calculation engine.
Bazi (Four Pillars / 八字) astrology system.
Bazi, also known as Four Pillars of Destiny, is a Chinese astrological system that uses the year, month, day, and hour of birth to create a chart of eight characters (four pairs of Heavenly Stem + Earthly Branch).
Example
>>> from stellium.chinese.bazi import BaZiEngine
>>> from datetime import datetime
>>>
>>> engine = BaZiEngine(timezone_offset_hours=-8) # PST
>>> chart = engine.calculate(datetime(1994, 1, 6, 11, 47))
>>>
>>> print(chart.hanzi) # The eight characters
>>> print(chart.day_master) # The Day Master (self)
>>> print(chart.display()) # Formatted table
>>> print(chart.display_detailed()) # With hidden stems and Ten Gods
- stellium.chinese.bazi.BaZiCalculator
alias of
BaZiEngine
- class stellium.chinese.bazi.BaZiChart(year, month, day, hour, birth_datetime)[source]
Bases:
objectA complete Four Pillars (Bazi / 八字) chart.
The Day Stem represents the “Day Master” (日主), which is the self.
Implements the ChineseChart protocol.
- property all_branches: tuple[EarthlyBranch, ...]
All four earthly branches.
- property all_hidden_stems: list[HeavenlyStem]
All hidden stems across all four pillars.
- property all_stems: tuple[HeavenlyStem, ...]
All four heavenly stems.
- birth_datetime: datetime
- day: Pillar
- property day_master: HeavenlyStem
The Day Master (日主) - the stem that represents the self.
- property day_master_element: Element
The element of the Day Master.
- display_detailed()[source]
Detailed prose display including hidden stems and Ten Gods.
- Return type:
- element_counts(include_hidden=False)[source]
Count occurrences of each element across stems and branches.
- Parameters:
include_hidden (
bool) – If True, includes hidden stems in the count. Hidden stems are weighted: main=1.0, middle=0.5, residual=0.3- Return type:
Note: For weighted hidden stem analysis, use element_strength() instead.
- property hanzi: str
The eight characters (八字) in Chinese.
- hour: Pillar
- month: Pillar
- property pillars: tuple[Pillar, Pillar, Pillar, Pillar]
year, month, day, hour.
- Type:
All four pillars in order
- polarity_counts()[source]
Count Yin vs Yang across all stems and branches.
- strength()[source]
Analyze the Day Master’s strength.
Returns a StrengthAnalysis with classification (Strong/Weak/etc.), component scores, and favorable/unfavorable elements.
Example:
bazi = ChartBuilder.from_details("1994-01-06 11:47", "Palo Alto, CA").bazi() result = bazi.strength() print(result.strength.english) # "Strong", "Weak", etc. print(result.favorable_elements)
- Return type:
StrengthAnalysis
- property system_name: str
The name of this system.
- ten_gods(include_hidden=True)[source]
Analyze Ten Gods (十神) relationships in the chart.
- Parameters:
include_hidden (
bool) – Whether to include hidden stems in analysis- Returns:
List of TenGodRelation objects
- to_dict()[source]
Export chart data as a dictionary (JSON-serializable).
- year: Pillar
- class stellium.chinese.bazi.BaZiEngine(timezone_offset_hours=0.0)[source]
Bases:
objectCalculate Bazi (Four Pillars) charts.
Implements the ChineseChartEngine protocol.
Example
>>> from datetime import datetime >>> engine = BaZiEngine(timezone_offset_hours=-8) # PST >>> chart = engine.calculate(datetime(1994, 1, 6, 11, 47)) >>> print(chart.display())
- calculate(birth_datetime)[source]
Calculate the Four Pillars chart for a birth datetime.
- Parameters:
birth_datetime (
datetime) – The birth date and time. Should be in local time if timezone_offset was provided, otherwise UTC.- Return type:
BaZiChart- Returns:
Complete BaZiChart with all four pillars.
- property system_name: str
The name of the system this engine calculates.
- class stellium.chinese.bazi.BaziProseRenderer(bullet='•')[source]
Bases:
objectRender Bazi charts as natural language prose.
Designed for pasting into conversations or documents.
Example
>>> renderer = BaziProseRenderer() >>> prose = renderer.render(chart) >>> print(prose)
- render(chart, include_hidden_stems=True, include_ten_gods=True)[source]
Render chart as prose text.
- class stellium.chinese.bazi.BaziRichRenderer[source]
Bases:
objectRender Bazi charts using Rich library for beautiful terminal output.
Requires: pip install rich
Example
>>> from stellium.chinese.bazi import BaZiEngine >>> from stellium.chinese.bazi.renderers import BaziRichRenderer >>> from datetime import datetime >>> >>> engine = BaZiEngine(timezone_offset_hours=-8) >>> chart = engine.calculate(datetime(1994, 1, 6, 11, 47)) >>> >>> renderer = BaziRichRenderer() >>> renderer.print_chart(chart) # Prints to terminal
- print_chart(chart, show_hidden_stems=True, show_ten_gods=True, show_summary=True)[source]
Print Bazi chart to terminal with Rich formatting.
- class stellium.chinese.bazi.BaziSVGRenderer(width=600, height=400, font_family='Noto Sans SC, SimSun, Microsoft YaHei, sans-serif')[source]
Bases:
objectRender Bazi charts as SVG images.
Creates a visual representation of the Four Pillars with: - Color-coded elements - Hidden stems shown below main characters - Ten Gods labels - Element balance visualization
Example
>>> renderer = BaziSVGRenderer() >>> svg_content = renderer.render(chart) >>> with open("chart.svg", "w") as f: ... f.write(svg_content)
- ELEMENT_COLORS = {Element.EARTH: '#795548', Element.FIRE: '#f44336', Element.METAL: '#9e9e9e', Element.WATER: '#2196f3', Element.WOOD: '#4caf50'}
- render(chart, show_hidden_stems=True, show_ten_gods=True, title=None)[source]
Render chart as SVG string.
- class stellium.chinese.bazi.DayMasterStrength(english, hanzi, pinyin)[source]
Bases:
EnumClassification of Day Master strength.
- MODERATE = ('Moderate', '中和', 'zhōng hé')
- STRONG = ('Strong', '旺', 'wàng')
- VERY_STRONG = ('Very Strong', '极旺', 'jí wàng')
- VERY_WEAK = ('Very Weak', '极弱', 'jí ruò')
- WEAK = ('Weak', '弱', 'ruò')
- class stellium.chinese.bazi.Pillar(stem, branch)[source]
Bases:
objectA single pillar (柱) consisting of a Stem and Branch.
- property animal: str
The zodiac animal of the branch.
- branch: EarthlyBranch
- property branch_element: Element
The element of the branch.
- property hanzi: str
The two-character Chinese representation.
- property hidden_stems: list[HeavenlyStem]
The hidden stems (藏干) within the branch.
- property pinyin: str
Pinyin romanization.
- stem: HeavenlyStem
- property stem_element: Element
The element of the stem (primary element of the pillar).
- class stellium.chinese.bazi.StrengthAnalysis(day_master, day_master_element, strength, score, seasonal_score, root_count, support_count, drain_count, month_branch)[source]
Bases:
objectComplete Day Master strength analysis result.
- day_master: HeavenlyStem
- day_master_element: Element
- drain_count: int
- property favorable_elements: list[Element]
Elements that are favorable for this Day Master.
If strong: needs Output, Wealth, Power to drain excess If weak: needs Companion, Resource to build strength
- property is_strong: bool
Whether the Day Master is considered strong (旺 or 极旺).
- property is_weak: bool
Whether the Day Master is considered weak (弱 or 极弱).
- month_branch: EarthlyBranch
- root_count: float
- score: float
- seasonal_score: int
- strength: DayMasterStrength
- support_count: int
- property unfavorable_elements: list[Element]
Elements that are unfavorable for this Day Master.
Opposite of favorable.
- class stellium.chinese.bazi.TenGod(english, chinese, hanzi, short_code)[source]
Bases:
EnumThe Ten Gods (十神) relationship types.
Each value contains: (english_name, chinese_name, hanzi, short_code)
- BI_JIAN = ('Friend', '比肩', '比', 'BJ')
- JIE_CAI = ('Rob Wealth', '劫财', '劫', 'JC')
- PIAN_CAI = ('Indirect Wealth', '偏财', '偏财', 'PC')
- PIAN_YIN = ('Indirect Seal', '偏印', '枭', 'PY')
- QI_SHA = ('Seven Killings', '七杀', '杀', 'QS')
- SELF = ('Self', '日主', '我', 'DM')
- SHANG_GUAN = ('Hurting Officer', '伤官', '伤', 'SG')
- SHI_SHEN = ('Eating God', '食神', '食', 'SS')
- ZHENG_CAI = ('Direct Wealth', '正财', '正财', 'ZC')
- ZHENG_GUAN = ('Direct Officer', '正官', '官', 'ZG')
- ZHENG_YIN = ('Direct Seal', '正印', '印', 'ZY')
- property category: str
The category this god belongs to.
- property is_direct: bool
Whether this is a ‘direct/正’ relationship (different polarities).
- class stellium.chinese.bazi.TenGodRelation(stem, ten_god, pillar_name, is_hidden=False)[source]
Bases:
objectA Ten God relationship for a specific stem in the chart.
- property display: str
Human-readable display.
- is_hidden: bool = False
- pillar_name: str
- stem: HeavenlyStem
- ten_god: TenGod
- stellium.chinese.bazi.analyze_strength(chart)[source]
Analyze the Day Master’s strength in the chart.
This is the main entry point for strength analysis.
- Parameters:
chart (
BaZiChart) – A calculated BaZiChart- Return type:
StrengthAnalysis- Returns:
Complete StrengthAnalysis with classification and component scores
Example
>>> from stellium.chinese import BaZiEngine >>> engine = BaZiEngine(timezone_offset_hours=8) >>> chart = engine.calculate(datetime(1990, 5, 15, 10, 30)) >>> result = analyze_strength(chart) >>> print(result.display())
- stellium.chinese.bazi.analyze_ten_gods(chart, include_hidden=True)[source]
Analyze all Ten God relationships in a Bazi chart.
- stellium.chinese.bazi.calculate_ten_god(day_master, other_stem)[source]
Calculate the Ten God relationship between Day Master and another stem.
- Parameters:
day_master (
HeavenlyStem) – The Day Master (日主) stemother_stem (
HeavenlyStem) – The stem to analyze
- Return type:
TenGod- Returns:
The TenGod relationship
- stellium.chinese.bazi.count_ten_god_categories(relations)[source]
Count Ten Gods by category (Companion, Output, Wealth, Power, Resource).
- stellium.chinese.bazi.count_ten_gods(relations)[source]
Count occurrences of each Ten God in the chart.
Bazi (Four Pillars) calculation engine.
This module implements the traditional calculation methods: - Year Pillar: Based on the Chinese year (starting at Li Chun) - Month Pillar: Based on solar terms, using the “Five Tigers” (五虎遁) formula - Day Pillar: Based on the day count from a reference date - Hour Pillar: Based on the two-hour periods, using “Five Rats” (五鼠遁) formula
Reference date for day pillar: Feb 12, 1900 = Jia Zi day (甲子日)
Implements the ChineseChartEngine protocol.
- stellium.chinese.bazi.engine.BaZiCalculator
alias of
BaZiEngine
- class stellium.chinese.bazi.engine.BaZiEngine(timezone_offset_hours=0.0)[source]
Bases:
objectCalculate Bazi (Four Pillars) charts.
Implements the ChineseChartEngine protocol.
Example
>>> from datetime import datetime >>> engine = BaZiEngine(timezone_offset_hours=-8) # PST >>> chart = engine.calculate(datetime(1994, 1, 6, 11, 47)) >>> print(chart.display())
- calculate(birth_datetime)[source]
Calculate the Four Pillars chart for a birth datetime.
- Parameters:
birth_datetime (
datetime) – The birth date and time. Should be in local time if timezone_offset was provided, otherwise UTC.- Return type:
BaZiChart- Returns:
Complete BaZiChart with all four pillars.
- property system_name: str
The name of the system this engine calculates.
- stellium.chinese.bazi.engine.hour_to_branch_index(hour, minute=0)[source]
Convert clock hour to Earthly Branch index.
The Chinese day starts at 23:00 (Zi hour). Each branch covers 2 hours: - Zi (子): 23:00-00:59 - Chou (丑): 01:00-02:59 - Yin (寅): 03:00-04:59 - etc.
Bazi (Four Pillars) chart data models.
A Bazi chart consists of four pillars: - Year Pillar (年柱) - represents ancestors, early childhood - Month Pillar (月柱) - represents parents, career - Day Pillar (日柱) - represents self, spouse - Hour Pillar (时柱) - represents children, later life
Each pillar has a Heavenly Stem and an Earthly Branch.
Implements the ChineseChart protocol for interoperability with other systems.
- class stellium.chinese.bazi.models.BaZiChart(year, month, day, hour, birth_datetime)[source]
Bases:
objectA complete Four Pillars (Bazi / 八字) chart.
The Day Stem represents the “Day Master” (日主), which is the self.
Implements the ChineseChart protocol.
- property all_branches: tuple[EarthlyBranch, ...]
All four earthly branches.
- property all_hidden_stems: list[HeavenlyStem]
All hidden stems across all four pillars.
- property all_stems: tuple[HeavenlyStem, ...]
All four heavenly stems.
- birth_datetime: datetime
- day: Pillar
- property day_master: HeavenlyStem
The Day Master (日主) - the stem that represents the self.
- property day_master_element: Element
The element of the Day Master.
- display_detailed()[source]
Detailed prose display including hidden stems and Ten Gods.
- Return type:
- element_counts(include_hidden=False)[source]
Count occurrences of each element across stems and branches.
- Parameters:
include_hidden (
bool) – If True, includes hidden stems in the count. Hidden stems are weighted: main=1.0, middle=0.5, residual=0.3- Return type:
Note: For weighted hidden stem analysis, use element_strength() instead.
- property hanzi: str
The eight characters (八字) in Chinese.
- hour: Pillar
- month: Pillar
- property pillars: tuple[Pillar, Pillar, Pillar, Pillar]
year, month, day, hour.
- Type:
All four pillars in order
- polarity_counts()[source]
Count Yin vs Yang across all stems and branches.
- strength()[source]
Analyze the Day Master’s strength.
Returns a StrengthAnalysis with classification (Strong/Weak/etc.), component scores, and favorable/unfavorable elements.
Example:
bazi = ChartBuilder.from_details("1994-01-06 11:47", "Palo Alto, CA").bazi() result = bazi.strength() print(result.strength.english) # "Strong", "Weak", etc. print(result.favorable_elements)
- Return type:
StrengthAnalysis
- property system_name: str
The name of this system.
- ten_gods(include_hidden=True)[source]
Analyze Ten Gods (十神) relationships in the chart.
- Parameters:
include_hidden (
bool) – Whether to include hidden stems in analysis- Returns:
List of TenGodRelation objects
- to_dict()[source]
Export chart data as a dictionary (JSON-serializable).
- year: Pillar
- class stellium.chinese.bazi.models.Pillar(stem, branch)[source]
Bases:
objectA single pillar (柱) consisting of a Stem and Branch.
- property animal: str
The zodiac animal of the branch.
- branch: EarthlyBranch
- property branch_element: Element
The element of the branch.
- property hanzi: str
The two-character Chinese representation.
- property hidden_stems: list[HeavenlyStem]
The hidden stems (藏干) within the branch.
- property pinyin: str
Pinyin romanization.
- stem: HeavenlyStem
- property stem_element: Element
The element of the stem (primary element of the pillar).
Ten Gods (十神) analysis for Bazi charts.
The Ten Gods represent the relationship between the Day Master (日主) and other stems in the chart. Each relationship has both productive and challenging aspects.
The Ten Gods are determined by two factors: 1. Element relationship (same, produces, produced-by, controls, controlled-by) 2. Polarity match (same polarity = “indirect/偏”, different polarity = “direct/正”)
Note: The Day Master stem always has relationship “Self” (日主/我) with itself.
- class stellium.chinese.bazi.analysis.TenGod(english, chinese, hanzi, short_code)[source]
Bases:
EnumThe Ten Gods (十神) relationship types.
Each value contains: (english_name, chinese_name, hanzi, short_code)
- BI_JIAN = ('Friend', '比肩', '比', 'BJ')
- JIE_CAI = ('Rob Wealth', '劫财', '劫', 'JC')
- PIAN_CAI = ('Indirect Wealth', '偏财', '偏财', 'PC')
- PIAN_YIN = ('Indirect Seal', '偏印', '枭', 'PY')
- QI_SHA = ('Seven Killings', '七杀', '杀', 'QS')
- SELF = ('Self', '日主', '我', 'DM')
- SHANG_GUAN = ('Hurting Officer', '伤官', '伤', 'SG')
- SHI_SHEN = ('Eating God', '食神', '食', 'SS')
- ZHENG_CAI = ('Direct Wealth', '正财', '正财', 'ZC')
- ZHENG_GUAN = ('Direct Officer', '正官', '官', 'ZG')
- ZHENG_YIN = ('Direct Seal', '正印', '印', 'ZY')
- property category: str
The category this god belongs to.
- property is_direct: bool
Whether this is a ‘direct/正’ relationship (different polarities).
- class stellium.chinese.bazi.analysis.TenGodRelation(stem, ten_god, pillar_name, is_hidden=False)[source]
Bases:
objectA Ten God relationship for a specific stem in the chart.
- property display: str
Human-readable display.
- is_hidden: bool = False
- pillar_name: str
- stem: HeavenlyStem
- ten_god: TenGod
- stellium.chinese.bazi.analysis.analyze_ten_gods(chart, include_hidden=True)[source]
Analyze all Ten God relationships in a Bazi chart.
- stellium.chinese.bazi.analysis.calculate_ten_god(day_master, other_stem)[source]
Calculate the Ten God relationship between Day Master and another stem.
- Parameters:
day_master (
HeavenlyStem) – The Day Master (日主) stemother_stem (
HeavenlyStem) – The stem to analyze
- Return type:
TenGod- Returns:
The TenGod relationship
- stellium.chinese.bazi.analysis.count_ten_god_categories(relations)[source]
Count Ten Gods by category (Companion, Output, Wealth, Power, Resource).
- stellium.chinese.bazi.analysis.count_ten_gods(relations)[source]
Count occurrences of each Ten God in the chart.
- stellium.chinese.bazi.analysis.get_element_relationship(day_master_element, other_element)[source]
Determine the Wu Xing relationship between Day Master and another element.
Returns one of: “same”, “produces”, “produced_by”, “controls”, “controlled_by”
- Return type:
- stellium.chinese.bazi.analysis.get_ten_gods_for_pillar(relations, pillar_name)[source]
Get all Ten God relations for a specific pillar (including hidden stems).
Renderers for Bazi (Four Pillars) charts.
This module provides different output formats for Bazi charts: - BaziRichRenderer: Beautiful terminal output using Rich library - BaziSVGRenderer: Visual SVG chart rendering - BaziProseRenderer: Natural language prose output
- class stellium.chinese.bazi.renderers.BaziProseRenderer(bullet='•')[source]
Bases:
objectRender Bazi charts as natural language prose.
Designed for pasting into conversations or documents.
Example
>>> renderer = BaziProseRenderer() >>> prose = renderer.render(chart) >>> print(prose)
- render(chart, include_hidden_stems=True, include_ten_gods=True)[source]
Render chart as prose text.
- class stellium.chinese.bazi.renderers.BaziRichRenderer[source]
Bases:
objectRender Bazi charts using Rich library for beautiful terminal output.
Requires: pip install rich
Example
>>> from stellium.chinese.bazi import BaZiEngine >>> from stellium.chinese.bazi.renderers import BaziRichRenderer >>> from datetime import datetime >>> >>> engine = BaZiEngine(timezone_offset_hours=-8) >>> chart = engine.calculate(datetime(1994, 1, 6, 11, 47)) >>> >>> renderer = BaziRichRenderer() >>> renderer.print_chart(chart) # Prints to terminal
- print_chart(chart, show_hidden_stems=True, show_ten_gods=True, show_summary=True)[source]
Print Bazi chart to terminal with Rich formatting.
- class stellium.chinese.bazi.renderers.BaziSVGRenderer(width=600, height=400, font_family='Noto Sans SC, SimSun, Microsoft YaHei, sans-serif')[source]
Bases:
objectRender Bazi charts as SVG images.
Creates a visual representation of the Four Pillars with: - Color-coded elements - Hidden stems shown below main characters - Ten Gods labels - Element balance visualization
Example
>>> renderer = BaziSVGRenderer() >>> svg_content = renderer.render(chart) >>> with open("chart.svg", "w") as f: ... f.write(svg_content)
- ELEMENT_COLORS = {Element.EARTH: '#795548', Element.FIRE: '#f44336', Element.METAL: '#9e9e9e', Element.WATER: '#2196f3', Element.WOOD: '#4caf50'}
- render(chart, show_hidden_stems=True, show_ten_gods=True, title=None)[source]
Render chart as SVG string.
CLI (stellium.cli)¶
Command-line interface for chart generation, cache management, and ephemeris downloads.