Skip to main content

Library to compute astronomical bodies positions and planetary aspects between them

Project description

Ketu

PyPI version Python Versions License: MIT

Ketu is a pure NumPy library for astronomical calculations focused on planetary positions, aspects, and cycle analysis. With no dependencies beyond NumPy, Ketu provides fast, accurate calculations suitable for astrology, biodynamic calendars, and machine learning applications.

This library was originally designed to generate biodynamic calendars and time series based on astrological aspects. It can be used as a basis for building astrology software.

Terminal screen

What's New in v1.3.0

Ketu v1.3.0 adds Chiron as the 14th body and makes the aspect engine data-driven. The public API is additive; the one breaking change is the internal positional-array contract (the bodies axis goes 13 → 14) and the aspect default/coefficient/preset surface. See UPGRADING.md for migration recipes and CHANGELOG.md for the full list.

  • Chiron — the 14th body (body_id = 13), evaluated from embedded Chebyshev-by-segment coefficients in pure NumPy (no pyswisseph, no SciPy, no new runtime dependency). ketu.calc_planet_position(jd, 13) returns Chiron's longitude within ~0.006° of Swiss Ephemeris across 1950–2050. Chiron participates in compute_chart, aspect detection, and the cycle machinery like any other body.
  • Data-driven aspect engine — aspects now live in a single declarative table (name, angle, coef, harmonic, symbol); the detection logic iterates over it with no per-aspect hardcoding. Compose a set from harmonics with aspects_for_harmonics([1, 2, 3, 6]), or use the CLASSICAL / TRADITIONAL / EXTENDED presets.
  • New default aspect set (breaking) — the library default is now the 7 half-circle harmonics (H1/H2/H3/H6: Conjunction, Semi-sextile, Sextile, Square, Trine, Quincunx, Opposition). The full-circle minors (quintile, novile, decile, and friends — H5/H9/H10) are opt-in. The CLI stays pinned to the classical 5 for byte-stable output.
  • Full French documentation — every Sphinx page is now fully translated to French through the gettext pipeline.

What's New in v1.2.0

Ketu v1.2.0 is a non-breaking feature release — all v1.1 code works unchanged. See UPGRADING.md for opt-in migration recipes and CHANGELOG.md for the full list of changes.

  • Synastryketu.synastry.calculate_synastry(chart_a, chart_b) returns a SYNASTRY_DTYPE cross-product of inter-chart aspects (15 bodies × 15 bodies = 225 pairs; filtered and dense modes). Astrodienst-style orbs (0.5× tightening). CLI ketu synastry; ketu --list-orbs.
  • Composite chartsketu.composite.calculate_composite(chart_a, chart_b) returns a CHART_DTYPE midpoint composite. Helper circular_midpoint(lon_a, lon_b) with pinned regression mid(359°, 1°) == 0°.
  • Solar and Lunar Returnsketu.returns.solar_return(..., target_year=<int>) and ketu.returns.lunar_return(..., target_jd=<float>) with arc-second convergence and optional relocation (return_lat/return_lon). API asymmetry: solar takes an integer year, lunar takes a Julian Date.
  • Arabic Partsketu.parts.calculate_part(name, chart) with sect-aware dispatch (Fortune / Spirit) and fixed Marriage formula; calculate_all_parts(chart) dict; ketu --list-parts.
  • Three new house systems — Whole Sign ("whole_sign"), Equal ("equal"), Regiomontanus ("regiomontanus") registered in ketu.houses.SYSTEMS. ketu --list-house-systems now returns six entries.
  • CI doc gates hardenedinterrogate ≥95% and numpydoc validate are both fully blocking; make doc-gates runs them locally. 214 pre-existing GL01 violations fixed.
  • GitHub Actions workflow refresh — Node.js 24 actions (checkout@v5, setup-python@v6, upload-artifact@v5); all Node 20 deprecation warnings eliminated.

For the full list of changes see CHANGELOG.md.

Features

  • Planetary positions for 14 bodies (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Rahu/Mean Node, True North Node, Lilith, Chiron)
  • Data-driven aspect engine — a declarative table of 14 major/minor aspects (Conjunction, Opposition, Trine, Square, Sextile, Quincunx, ... through Quintile, Novile, Decile); harmonic-based selection via aspects_for_harmonics([...]). The default set is the 7 half-circle aspects; full-circle minors are opt-in.
  • Aspect windows - Find when aspects begin, peak, and end
  • Transit calculations - Track transits to natal positions
  • Retrogradation detection and planet motion helpers
  • Time system conversions (UTC, Julian Day)
  • Orb system based on Abu Ma'shar (787-886) and Al-Biruni (973-1050)
  • Interactive CLI for a non-programmatic workflow
  • Python API that fits into your own tooling
  • Pure NumPy - Single dependency for maximum portability and performance

Installation

From PyPI (recommended)

pip install ketu

From source

git clone https://github.com/alkimya/ketu.git
cd ketu
pip install -e .

Quick Start

Interactive mode (CLI)

Run the command below and answer the prompts:

ketu

You will be asked for:

  • A date (ISO format: 2020-12-21)
  • A time (ISO format: 19:20)
  • A timezone (for example Europe/Paris)

The program prints:

  • Positions of every celestial body with zodiac signs
  • All inter-planet aspects with their orbs

Programmatic usage

from datetime import datetime
from zoneinfo import ZoneInfo
import ketu

# Define a datetime
dtime = datetime(2020, 12, 21, 19, 20, tzinfo=ZoneInfo("Europe/Paris"))
jday = ketu.utc_to_julian(dtime)

# Display planetary positions
ketu.print_positions(jday)

# Display aspects
ketu.print_aspects(jday)

Advanced Examples

Compute a planet position

from datetime import datetime
from zoneinfo import ZoneInfo
import ketu

dtime = datetime(2024, 10, 26, 12, 0, tzinfo=ZoneInfo("UTC"))
jday = ketu.utc_to_julian(dtime)

sun_long = ketu.long(jday, 0)
print(f"Sun longitude: {sun_long:.2f}°")

sign, deg, mins, secs = ketu.body_sign(sun_long)
print(f"Position: {ketu.signs[sign]} {deg}°{mins}'{secs}\"")

Check whether a planet is retrograde

import ketu

# Mars (body id = 4)
if ketu.is_retrograde(jday, 4):
    print("Mars is retrograde")
else:
    print("Mars is direct")

Find aspect windows

from datetime import datetime, timedelta
import ketu

# Find Sun-Moon conjunction window
start = ketu.utc_to_julian(datetime(2025, 1, 1, tzinfo=ZoneInfo("UTC")))
end = ketu.utc_to_julian(datetime(2025, 12, 31, tzinfo=ZoneInfo("UTC")))

windows = ketu.find_aspect_window(start, end, body1=0, body2=1, aspect=0)

for window in windows:
    print(f"Conjunction from {ketu.julian_to_utc(window.begin_jd)} "
          f"to {ketu.julian_to_utc(window.end_jd)}")
    print(f"  Exact: {ketu.julian_to_utc(window.exact_jd)}")

Calculate transits to natal positions

import ketu

# Natal positions
natal_date = ketu.utc_to_julian(datetime(1990, 1, 15, 12, 0, tzinfo=ZoneInfo("UTC")))
natal_positions = ketu.get_natal_positions(natal_date)

# Find transits for a specific date
transit_date = ketu.utc_to_julian(datetime(2025, 11, 22, 12, 0, tzinfo=ZoneInfo("UTC")))
transits = ketu.compare_dates_transits(natal_positions, transit_date)

for transit in transits:
    print(f"{transit.transiting_body} {transit.aspect} natal {transit.natal_body}")

Ephemeris Cache (v0.4.0)

For ML pipelines and high-frequency lookups, use the ephemeris cache for 1000x faster position lookups:

from ketu.cache import EphemerisCache
from datetime import datetime, timezone

# Initialize cache (stores in ~/.ketu/ephemeris_cache/)
cache = EphemerisCache()

# Pre-compute a range of months (one-time operation)
# ~1-2 seconds per month, persisted to disk
for year in range(2020, 2026):
    for month in range(1, 13):
        cache.ensure_month(year, month)

# Fast O(1) lookups (0.006ms vs 10ms computation)
timestamp = datetime(2025, 6, 15, 14, 30, tzinfo=timezone.utc)

# Get single body position (lon, lat, distance, speed)
sun_pos = cache.get_position(timestamp, body_id=0)
print(f"Sun longitude: {sun_pos[0]:.2f}°")

# Get all 14 bodies at once
all_positions = cache.get_all_positions(timestamp)
# Returns dict: {body_id: (lon, lat, dist, speed), ...}

CLI for pre-computing cache:

# Pre-compute 2020-2030 (takes ~3-4 minutes)
python scripts/precompute_ephemeris.py --years 2020-2030

# Single year
python scripts/precompute_ephemeris.py --year 2025

# Force recompute
python scripts/precompute_ephemeris.py --year 2025 --force

Performance:

  • Lookup: 0.006ms (with interpolation)
  • Compute: 10ms
  • Speedup: 1000x
  • Disk usage: ~50KB per month

Documentation

The full documentation is hosted on Read the Docs.

Included sections:

  • Installation: detailed setup instructions
  • Quickstart: guided tour of the basics
  • Concepts: astrological and astronomical background
  • API Reference: all functions documented
  • Examples: advanced usage patterns
  • Developer Guide: architecture and performance details

Documentation Quality Gates

Documentation quality is enforced by CI on every push:

  • interrogate ≥95% (blocking) — every public function, class, and module has a docstring.
  • numpydoc validate (warning, blocking from v1.2.0) — docstrings follow the NumPy convention.

Run both locally before pushing: make doc-gates.

Requirements

  • Python 3.10 or higher
  • numpy ≥ 1.20.0 — numerical routines and arrays

That's it! Ketu has no other dependencies.

Supported bodies

Body ID Orb Average speed (°/day)
Sun 0 12° 0.986
Moon 1 12° 13.176
Mercury 2 1.383
Venus 3 10° 1.200
Mars 4 0.524
Jupiter 5 10° 0.083
Saturn 6 10° 0.034
Uranus 7 0.012
Neptune 8 0.007
Pluto 9 0.004
Rahu (Mean Node) 10 -0.013
True North Node 11 -0.013
Lilith (Black Moon) 12 -0.113
Chiron 13 0.019

Supported aspects

Aspect Angle Orb coefficient
Conjunction 1
Semi-sextile 30° 1/6
Sextile 60° 1/3
Square 90° 1/2
Trine 120° 2/3
Quincunx 150° 5/6
Opposition 180° 1

These 7 half-circle aspects (harmonics 1, 2, 3, 6) are the library default. The full table also carries 7 full-circle minor aspects (Quintile, Decile, Novile, Binovile, Quadrinovile, Biquintile, Tredecile — harmonics 5, 9, 10), which are opt-in via aspects_for_harmonics([...]) or the EXTENDED preset.

Performance

The pure NumPy implementation provides excellent performance:

  • Time series (365 days): 208x faster than loop-based approach
  • Aspect calculations: 14.55x faster with vectorization
  • Single planet position: 67x faster with optimized algorithms
  • Moon position: 59x faster with custom perturbation calculations

See docs/en/performance.md for detailed benchmarks.

Accuracy

The implementation provides good accuracy for astrological purposes:

  • Planetary positions: ±0.1° for inner planets, ±0.5° for outer planets
  • Moon position: ±0.5° (includes major perturbations)
  • Aspect timing: ±2 minutes for exact aspects
  • Best accuracy range: 1800-2200 CE

Architecture

ketu/
├── __init__.py          # Main API
├── core.py              # Data structures (bodies, aspects, signs)
├── calculations.py      # High-level calculation functions
├── complex.py           # Complex-number engine for cycle analysis
├── display.py           # Display utilities
├── lunar_calendar.py    # Biodynamic / lunar calendar helpers
├── aspects/             # Data-driven aspect engine (presets, harmonics)
├── charts/              # compute_chart / CHART_DTYPE abstraction
├── houses/              # Six house systems (Placidus, Whole Sign, ...)
├── synastry/            # Inter-chart aspect cross-products
├── composite/           # Midpoint composite charts
├── returns/             # Solar and lunar returns
├── parts/               # Arabic Parts framework
├── cycles/              # Planetary cycle series (NumPy structured arrays)
├── cli/                 # Interactive command-line interface
├── cache/               # High-performance ephemeris cache
├── data/                # Embedded Chiron Chebyshev coefficients (.npz)
└── ephemeris/           # Astronomical calculations
    ├── time.py          # Time conversions
    ├── orbital.py       # Orbital mechanics (re-export hub)
    ├── coordinates.py   # Coordinate transformations
    ├── planets.py       # Planetary position calculations (per-body strategies)
    └── chiron.py        # Pure-NumPy Chiron Chebyshev evaluator

Roadmap

  • Removal of dependency on pyswisseph
  • Pure numpy implementation of planetary calculations
  • Search for exact aspects between two dates
  • Aspect windows and timing
  • Transit calculations
  • High-performance ephemeris cache
  • Complex number engine for cycle analysis
  • Configurable aspects and six house systems
  • Chart abstraction (compute_chart / CHART_DTYPE)
  • Relational charts (synastry, midpoint composite)
  • Predictive charts (solar and lunar returns)
  • Arabic Parts framework
  • Chiron as the 14th body (pure-NumPy Chebyshev evaluation)
  • Data-driven aspect engine with harmonic-based selection

Contribution

Contributions are welcome! Feel free to:

  • Open an issue to report a bug or suggest a feature
  • Submit a pull request
  • Improve the documentation

See CONTRIBUTING.md for more details.

License

This project is licensed under MIT. See the LICENSE file for more details.

Contact

Loc Cosnier - @alkimya

Project: https://github.com/alkimya/ketu

Acknowledgments

  • solarsystem by Ioannis Nasios — The pure Python astronomy library that inspired and served as the mathematical foundation for Ketu's NumPy ephemeris engine. Kepler's equation solver, perturbation terms, coordinate transformations, and Moon calculations all trace back to this elegant, dependency-free library. Thank you!
  • Claude by Anthropic — The pure NumPy rewrite, from orbital mechanics to aspect detection, was developed in collaboration with Claude. Architecture, algorithms, tests, documentation were produced through extensive pair programming sessions.
  • GSD (Get Shit Done) — The project management workflow that structured the development of Ketu v1.0.0 into phases with research, planning, execution, and verification steps.
  • Original orbital calculations based on Paul Schlyter's work
  • Inspired by the accuracy and reliability of Swiss Ephemeris
  • Built with the power of NumPy for scientific computing

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ketu-1.3.0.tar.gz (670.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ketu-1.3.0-py3-none-any.whl (491.0 kB view details)

Uploaded Python 3

File details

Details for the file ketu-1.3.0.tar.gz.

File metadata

  • Download URL: ketu-1.3.0.tar.gz
  • Upload date:
  • Size: 670.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ketu-1.3.0.tar.gz
Algorithm Hash digest
SHA256 f06f744712d359670f56d87c85654478bda895e0e25f9ad61eb6f1857ac142a8
MD5 6bf1e9ade18a55c8e3ade1bcf54f2996
BLAKE2b-256 30526adf2bde9f10655e1339587186c707de83beaae388b279dc1eadd488a9da

See more details on using hashes here.

Provenance

The following attestation bundles were made for ketu-1.3.0.tar.gz:

Publisher: publish.yml on alkimya/ketu

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ketu-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: ketu-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 491.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ketu-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 577c39fe906565da41afb283f91af2a98a0ddf21096b35ea1cda7f5e1af3aed3
MD5 31b8be014e05f52ce90948a81314c329
BLAKE2b-256 5dd55ac217b000810a57136df9e1e6a914a1ef74b4fb1b6f05a0189229a0b5fc

See more details on using hashes here.

Provenance

The following attestation bundles were made for ketu-1.3.0-py3-none-any.whl:

Publisher: publish.yml on alkimya/ketu

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page