Planetary Return Factory #
The PlanetaryReturnFactory calculates the precise moment when a planet returns to its natal position (e.g., Solar Returns). It uses the configured ephemeris backend (libephemeris by default, Swiss Ephemeris optional) for high-precision timing.
What Are Planetary Returns? #
A Planetary Return occurs when a transiting planet returns to the exact degree it occupied at birth. The return chart cast for that precise moment becomes a forecast for the cycle ahead:
Solar Return (Birthday Chart):
- Happens once per year on or near your birthday
- The Sun returns to its natal position (~365.25 days)
- Forecasts themes and events for the coming year
- Traditionally cast for your current location (relocated solar return)
Lunar Return:
- Happens approximately every 27-29 days
- The Moon returns to its natal position
- Forecasts the emotional climate for the coming month
- Useful for timing short-term events and emotional cycles
Kerykeion supports Solar, Lunar, Heliocentric, and Lunar Node Crossing returns.
Usage #
Calculating a return involves the Natal Subject and the Return Location (which can differ from birth location).
from kerykeion import AstrologicalSubjectFactory
from kerykeion.planetary_returns.factory import PlanetaryReturnFactory
# 1. Create Natal Subject
natal_subject = AstrologicalSubjectFactory.from_birth_data(
"Alice", 1990, 6, 15, 12, 0, "London", "GB"
)
# 2. Initialize Calculator (with Return Location)
# Here we calculate the return for New York
return_factory = PlanetaryReturnFactory(
natal_subject,
city="New York",
nation="US",
online=True
)
# 3. specific Return (e.g., Solar Return for 2024)
solar_return = return_factory.next_return_from_date(
year=2024,
month=1,
day=1, # Start searching from this date
return_type="Solar"
)
print(f"Return Date: {solar_return.iso_formatted_local_datetime}")
print(f"Sun Position: {solar_return.sun.abs_pos:.2f}°")
Expected Output:
Return Date: 2024-06-14T12:14:42-04:00
Sun Position: 84.09°
Note: The Sun position matches Alice’s natal Sun position exactly (within calculation precision).
Custom Ayanamsa in Returns #
When using sidereal_mode="USER" on the natal subject, pass the custom ayanamsa parameters to PlanetaryReturnFactory so the return chart uses the same ayanamsa:
from kerykeion import AstrologicalSubjectFactory
from kerykeion.planetary_returns.factory import PlanetaryReturnFactory
natal = AstrologicalSubjectFactory.from_birth_data(
"Sidereal User", 1990, 6, 15, 12, 0,
lng=-0.1276, lat=51.5074, tz_str="Europe/London",
online=False,
zodiac_type="Sidereal",
sidereal_mode="USER",
custom_ayanamsa_t0=2451545.0,
custom_ayanamsa_ayan_t0=23.5,
)
return_factory = PlanetaryReturnFactory(
natal,
city="London", nation="GB",
lng=-0.1276, lat=51.5074, tz_str="Europe/London",
online=False,
custom_ayanamsa_t0=2451545.0,
custom_ayanamsa_ayan_t0=23.5,
)
solar_return = return_factory.next_return_from_date(2024, 1, 1, return_type="Solar")
print(f"Return ayanamsa: {solar_return.ayanamsa_value:.4f}°")
When the natal subject uses
sidereal_mode="USER", passingcustom_ayanamsa_t0andcustom_ayanamsa_ayan_t0explicitly overrides the natal’s; leaving them out reads them from the natal subject. The constructor raisesKerykeionExceptiononly when neither source provides both.
Supported Return Types #
"Solar"(Sun) – Yearly forecast."Lunar"(Moon) – Monthly forecast."Heliocentric"– when a planet returns to its natal heliocentric longitude (see below)."Lunar_Node_Crossing"– when the Moon crosses its own node (latitude = 0; see below).
Note:
return_typevalues are case-sensitive (e.g. exactly"Solar").
Heliocentric Returns #
next_heliocentric_return(planet_name, start_jd, backwards=False) finds when a planet returns to its natal heliocentric longitude (via helio_cross_ut). The Sun and Moon are not valid targets — the Sun is the heliocentric origin and the Moon’s heliocentric longitude tracks Earth’s orbit — and raise KerykeionException. backwards=True requires the libephemeris backend (pyswisseph cannot search backward).
# Using the `return_factory` from the example above:
helio = return_factory.next_heliocentric_return_from_year("Jupiter", 2026)
print(helio.iso_formatted_utc_datetime)
Convenience wrappers mirror the Solar/Lunar ones: next_heliocentric_return_from_year(planet_name, year), next_heliocentric_return_from_date(planet_name, year, month, day=1, backwards=False), next_heliocentric_return_from_iso_formatted_time(planet_name, iso_formatted_time, backwards=False).
Lunar Node Crossings #
next_lunar_node_crossing(start_jd, backwards=False) finds the next moment the Moon crosses its own node (ecliptic latitude = 0; via mooncross_node_ut). It is zodiac-independent. backwards=True requires the libephemeris backend.
crossing = return_factory.next_lunar_node_crossing_from_year(2026)
print(crossing.iso_formatted_utc_datetime)
Convenience wrappers: next_lunar_node_crossing_from_year(year), next_lunar_node_crossing_from_date(year, month, day=1, backwards=False), next_lunar_node_crossing_from_iso_formatted_time(iso_formatted_time, backwards=False).
Constructor Parameters #
| Parameter | Type | Default | Description |
|---|---|---|---|
subject |
AstrologicalSubjectModel |
Required | The natal subject whose return is being calculated. |
city |
Optional[str] |
None |
City name for the return location. |
nation |
Optional[str] |
None |
ISO country code for the return location. |
lng |
int | float | None |
None |
Longitude of the return location. |
lat |
int | float | None |
None |
Latitude of the return location. |
tz_str |
Optional[str] |
None |
Timezone string for the return location. |
online |
bool |
True |
Whether to resolve location via GeoNames API. |
geonames_username |
Optional[str] |
None |
GeoNames username for online mode. |
cache_expire_after_days |
int |
30 |
Days to cache online location lookups. |
altitude |
int | float | None |
None |
Altitude in meters. Topocentric returns use the return location and altitude in both the exact-crossing search and the returned chart. |
custom_ayanamsa_t0 |
Optional[float] |
None |
Reference epoch (Julian Day) for USER sidereal mode. |
custom_ayanamsa_ayan_t0 |
Optional[float] |
None |
Ayanamsa offset at epoch (required with USER sidereal mode). |
active_fixed_stars |
List[str] | None |
None |
Fixed-star catalog names to compute on the return chart. |
calculate_dignities |
bool |
False |
Populate essential dignity data. |
calculate_nakshatra |
bool |
False |
Populate Vedic nakshatra data. |
nakshatra_ayanamsa |
Optional[SiderealMode] |
inherited | Ayanamsa used to place the nakshatras on a non-sidereal return chart. Passed explicitly it wins — None included, which is the pre-v6 uncorrected division. Left out, the return inherits the mode the natal actually placed its nakshatras with, and falls back to "LAHIRI" when the natal placed none. |
calculate_gauquelin |
bool |
False |
Populate Gauquelin sectors. |
calculate_nutation |
bool |
False |
Populate the nutation model. |
calculate_local_space |
bool |
False |
Populate local-space azimuth/altitude. |
Methods #
next_return_from_date(year, month, day=1, *, return_type, backwards=False) #
Finds a return starting from a specific year/month/day. This is the primary method.
result = return_factory.next_return_from_date(2025, 1, 1, return_type="Solar")
| Parameter | Type | Default | Description |
|---|---|---|---|
year |
int |
Required | Year to start searching from. |
month |
int |
Required | Month to start searching from. |
day |
int |
1 |
Day to start searching from. |
return_type |
SolarLunarReturnType |
Required | "Solar" or "Lunar" (keyword-only). |
backwards |
bool |
False |
If True, return the most recent return before the starting date instead of the next one. |
next_return_from_iso_formatted_time(iso_formatted_time, return_type, backwards=False) #
Finds a return starting from a precise ISO timestamp.
result = return_factory.next_return_from_iso_formatted_time("2024-06-15T12:00:00", "Lunar")
| Parameter | Type | Default | Description |
|---|---|---|---|
iso_formatted_time |
str |
Required | ISO 8601 timestamp at which to start the search (naive values are read as UTC). |
return_type |
SolarLunarReturnType |
Required | "Solar" or "Lunar". |
backwards |
bool |
False |
If True, search for the most recent return before the timestamp. |
Return instants are reported truncated to the whole second, and the search is
ordered at that resolution: the instant of a return this factory reported is a
valid seed for the following one (or, with backwards=True, the preceding one),
so a walk of next_return_from_iso_formatted_time calls seeded with each result’s
iso_formatted_utc_datetime visits every return exactly once. A seed inside the
same second as a crossing selects the return after it (forward) or before it
(backward). The date and year wrappers above keep an inclusive midnight seed: “the
first return of this date” includes a return in the date’s first second.
first = return_factory.next_return_from_iso_formatted_time("2024-03-01T00:00:00+00:00", "Solar")
second = return_factory.next_return_from_iso_formatted_time(first.iso_formatted_utc_datetime, "Solar")
assert second.julian_day > first.julian_day # the following return, not the same one again
Backward Solar/Lunar return search requires the default libephemeris backend;
the optional pyswisseph backend does not expose backward crossing searches and
raises KerykeionException for backwards=True.
previous_solar_return = return_factory.next_return_from_date(
2025, 1, 1,
return_type="Solar",
backwards=True,
)
print(previous_solar_return.iso_formatted_utc_datetime)
next_return_from_year(year, return_type) (Deprecated) #
Finds the first return occurring in a given calendar year. Deprecated – use next_return_from_date instead.
result = return_factory.next_return_from_year(2025, "Solar") # Deprecated
result = return_factory.next_return_from_date(2025, 1, 1, return_type="Solar") # Preferred
Both arguments are required; return_type is "Solar" or "Lunar". The
method emits DeprecationWarning, is scheduled for removal in 7.0.0, and
delegates to next_return_from_date(year, 1, 1, return_type=...).
next_return_from_month_and_year(year, month, return_type) (Deprecated) #
This older three-argument alias searches from the first day of month. It
emits DeprecationWarning, is scheduled for removal in 7.0.0, and delegates to
next_return_from_date(year, month, 1, return_type=...).
Relocation Astrology #
Planetary returns are often calculated for the subject’s current location rather than birth location. Simply pass the current residence coordinates/city to the PlanetaryReturnFactory constructor to generate a relocated return chart.