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.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 13 bodies (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Rahu/Mean Node, True North Node, Lilith)
  • Detection of 14 major/minor aspects (Conjunction, Opposition, Trine, Square, Sextile, Quintile, Novile, Decile, etc.)
  • 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 13 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

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

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
├── display.py           # CLI and display utilities
├── aspect_windows.py    # Aspect timing calculations
├── transits.py          # Transit calculations
├── cache/               # High-performance ephemeris cache
│   ├── __init__.py
│   └── ephemeris_cache.py  # Monthly pre-computed positions
└── ephemeris/           # Astronomical calculations
    ├── time.py          # Time conversions
    ├── orbital.py       # Orbital mechanics
    ├── coordinates.py   # Coordinate transformations
    └── planets.py       # Planetary position calculations

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

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.2.0.tar.gz (321.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.2.0-py3-none-any.whl (180.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: ketu-1.2.0.tar.gz
  • Upload date:
  • Size: 321.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.2.0.tar.gz
Algorithm Hash digest
SHA256 038ffdcb9bd2b00eeb5ca7d7e05169f12159960549658cfabd914e155d455ba6
MD5 874f63d41942c75895fe30a50d5d3da5
BLAKE2b-256 9effa819ac16a918689bba645a11ad5191f0cda35d79b7fad2d35eeb7e1739e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for ketu-1.2.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.2.0-py3-none-any.whl.

File metadata

  • Download URL: ketu-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 180.8 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.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2d8daee34a065a7c2fa4b520b03f026b79ba4a227a5af3372cbea50a02462938
MD5 e0794ac57b798c3076eb02a0fb30ac5a
BLAKE2b-256 4e2f4b434305c972a5a14c1d96f7429bd0d0d725cec4acdd15ec430ab32334c0

See more details on using hashes here.

Provenance

The following attestation bundles were made for ketu-1.2.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