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.
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 MoonsolarArc(engine,natalJd,targetJd);// arc in degreesdirectedLongitude(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";constbodies = ["sun","moon","mercury","venus","mars"];constjdB = 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 harmonicantiscion(15);// reflection across 0° Cancer / 0° CapricorncontraAntiscion(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 | falseoutOfBoundsMargin(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.constday = 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";constSIGNS = ["Aries","Taurus","Gemini","Cancer","Leo","Virgo","Libra","Scorpio","Sagittarius","Capricorn","Aquarius","Pisces"];consttoDate = (jd:number) =>newDate((jd - 2440587.5) * 86400000).toISOString().slice(0,10);// A decade of solar returns:constfrom = 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(letyear = 2026;year < 2038;year++){constjd = julianDay(year,6,10,0,0,0);constlon = 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.