Guides

Derived Charts

The derived layer is the standard astrological derivations computed on top of the validated primitives. These are time mappings and arithmetic on apparent positions that are already checked against Swiss Ephemeris, so they add no new ephemeris. The TypeScript port is pinned to the Python reference by a cross-language golden, and everything here is exported from the package root.

setup.ts
import { Engine, julianDay } from "caelus";
import { embeddedData } from "caelus/data-embedded";

const engine = new Engine(embeddedData);

const natalJd = julianDay(1990, 6, 10, 14, 30, 0);   // UT
const targetJd = julianDay(2026, 6, 13, 0, 0, 0);

Returns

A return is when a body comes back to its natal longitude. Outer-planet returns can show three crossings around a retrograde loop, so the search returns every hit in the window.

returns.ts
import { solarReturn, lunarReturn, returns } from "caelus";

// Next solar return after the 2026 birthday window:
solarReturn(engine, natalJd, targetJd, targetJd + 366);   // [jd, ...]
lunarReturn(engine, natalJd, targetJd, targetJd + 30);

// Any body, e.g. the Saturn return:
returns(engine, "saturn", natalJd, natalJd, natalJd + 365 * 30);

Secondary progressions and solar arc

Secondary progressions map one day of real motion to one year of life. progressedJd gives the JD whose real positions are the progressed positions; solarArc is the angle the progressed Sun has travelled, applied forward to any natal point.

progressions.ts
import { progressedLongitude, solarArc, directedLongitude } from "caelus";

progressedLongitude(engine, "moon", natalJd, targetJd);   // progressed Moon
solarArc(engine, natalJd, targetJd);                      // arc in degrees
directedLongitude(engine, "mars", natalJd, targetJd);     // solar-arc directed Mars

Composite and Davison

A composite chart takes the shorter-arc midpoint of each body's two longitudes. A Davison chart is a real chart at the midpoint in time and place: davisonParams returns the midpoint instant and coordinates, and you read real positions at them. The returned latitude and longitude are where the houses are cast.

relationship.ts
import { compositeLongitudes, davisonParams } from "caelus";

const bodies = ["sun", "moon", "mercury", "venus", "mars"];
const jdB = julianDay(1988, 2, 2, 9, 15, 0);

compositeLongitudes(engine, natalJd, jdB, bodies);   // { sun: 123.4, ... }

// Davison: the midpoint in time and place, then real positions there.
const [midJd, midLat, midLon] = davisonParams(
natalJd, jdB, 27.95, -82.46, 40.71, -74.01,
);
engine.longitude("sun", midJd);    // Davison Sun longitude (degrees)
engine.position("venus", midJd);   // full position at midLat/midLon

Harmonics and antiscia

The nth-harmonic chart multiplies each longitude by n (wrapped to 360). Antiscia reflect a point across the solstice or equinox axis.

harmonics.ts
import { harmonicChart, antiscion, contraAntiscion } from "caelus";

harmonicChart(engine, natalJd, ["sun", "moon", "venus"], 5);   // 5th harmonic

antiscion(15);        // reflection across 0° Cancer / 0° Capricorn
contraAntiscion(15);  // reflection across 0° Aries / 0° Libra

Declination: aspects and out of bounds

Parallels and contraparallels are declination aspects (same or opposite declination within orb). A body is out of bounds when its declination exceeds the obliquity of the ecliptic.

declination.ts
import { declinationAspects, outOfBounds, outOfBoundsMargin } from "caelus";

declinationAspects(engine, ["sun", "moon", "mars"], natalJd);
// [{ a: "sun", b: "mars", kind: "parallel" }, ...]

outOfBounds(engine, "moon", natalJd);        // true | false
outOfBoundsMargin(engine, "moon", natalJd);  // degrees past the obliquity

Dignities and sect

Essential dignities classify a body in a sign as domicile, exaltation, detriment, or fall. Sect is the day/night division: whether the Sun is above the horizon, and whether a planet is in its preferred sect.

dignities.ts
import { dignityOf, isDayChart, inSect } from "caelus";

dignityOf(engine, "mars", natalJd);   // ["domicile"] in Aries/Scorpio, etc.

const day = isDayChart(engine, natalJd, 27.95, -82.46);
inSect("jupiter", day);   // true when Jupiter is in sect (diurnal by day)

Timelines

The derivations compose into timelines: the returns search is already a span, and the progression mappings are pure functions of a target JD, so you sample them on whatever cadence you need. A JD converts back to a date with the standard epoch offset, no helper required.

timelines.ts
import { solarReturn, progressedLongitude } from "caelus";

const SIGNS = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"];
const toDate = (jd: number) =>
new Date((jd - 2440587.5) * 86400000).toISOString().slice(0, 10);

// A decade of solar returns:
const from = julianDay(2026, 1, 1, 0, 0, 0);
solarReturn(engine, natalJd, from, from + 365.25 * 10).map(toDate);
// ["2026-06-10", "2027-06-11", ...]

// Progressed-Moon sign at each birthday for the next twelve years:
for (let year = 2026; year < 2038; year++) {
const jd = julianDay(year, 6, 10, 0, 0, 0);
const lon = progressedLongitude(engine, "moon", natalJd, jd);
console.log(year, SIGNS[Math.floor(lon / 30)]);
}

The same shape drives a transit timeline (sample engine.longitude over a range) or a directed-aspect scan (directedLongitude against natal points). For century-scale sweeps that evaluate a longitude tens of thousands of times, the turbo tier finds candidate instants fast. For ready-made transit and event-search recipes, see Recipes.

See the API Reference for every signature, Edge Cases & Stability for range and stability notes, and the Changelog for what shipped in 0.7.0.