vedic_jyotish_completion - TheDaniel166/moira GitHub Wiki
Vedic Jyotish Systems β Pre-Phase 1 Implementation Roadmap
OUTDATED β historical implementation blueprint (6 October 2026). Preserve these source leads and design notes for provenance. Current remaining work is tracked in the Vedic remaining work register; the original absence claims below are not the current codebase state.
STATUS (2026-07-08): PHASE 1 COMPLETE. All seven systems below are implemented, constitutionalized, and served: varga (17 divisions), vedic_dignities, jaimini (karakas), panchanga, dasha_systems (Ashtottari + Yogini), ashtakavarga (incl. both Shodhanas), and shadbala β including Bhava Bala and Ishta/Kashta Phala, which Β§System 7 below had explicitly scoped out and which were closed on 2026-07-08. The "Codebase State" section below is the historical pre-Phase-1 snapshot, preserved for provenance. For remaining gaps, see Vedic remaining work register.
Codebase State (Verified)
Present and usable:
moira/sidereal.pyβtropical_to_sidereal,nakshatra_of,Ayanamsa,NAKSHATRA_LORDSmoira/varga.pyβcalculate_varga, wrappers for D7, D9, D10, D12, D30 onlymoira/dasha.pyβ Vimshottari only; no alternative systemsmoira/decanates.pyβ D3 viavedic_drekkana()moira/dignities.pyβ Western only; nothing Vedicmoira/cycles.pyβ_jd_to_weekday(),planetary_day_ruler()availablemoira/chart.pyβChartContext.planets,.nodes,.houses,.jd_ut,.is_day
None of the 7 target systems exist in any form.
Implementation Sequence (Dependency Order)
1. Shodashvarga Completion (varga.py extension) β no upstream deps
2. Vedic Planetary Dignities (new module) β no upstream deps
3. Jaimini Karakas (new module) β sidereal.py only
4. Panchanga (new module) β sidereal.py, cycles.py
5. Alternative Dasha Systems (new module) β sidereal.py, dasha.py patterns
6. Ashtakavarga (new module) β vedic_dignities.py (for Shodhana)
7. Shadbala (new module) β all of the above
Rationale: Shadbala is the most downstream β Saptavargaja Bala requires all 7 varga sign positions, Kala Bala requires Panchanga elements (Vara, Paksha, Hora), and Drig Bala optionally uses Ashtakavarga scores. Everything else is self-contained.
System 1 β Shodashvarga Completion
Primary Authority
Parashara, Brihat Parashara Hora Shastra, Shodashavarga Adhyaya (Ch. 6β22).
Validation Reference
Jhora (Jagannatha Hora), Parashara Light 9.x; B.V. Raman, How to Judge a Horoscope.
File
Extend moira/varga.py.
Missing Wrappers
D2, D4, D6, D8, D16, D20, D24, D27, D40, D45, D60.
Non-Generic Parashari Rules
| Varga | Rule |
|---|---|
| D2 Hora | Odd signs: 1st half (0Β°β15Β°) β Leo, 2nd half β Cancer. Even signs reversed. |
| D4 Chaturthamsha | d4_sign = (sign_idx + segment_in_sign) % 12 β starts from own sign, not Aries. |
| D27 Saptavimshamsha | Start from Aries (fire), Cancer (earth), Libra (air), Capricorn (water). |
| D40 Khavedamsha | Odd signs start Aries; even signs start Libra. |
| D45 Akshavedamsha | Odd signs start Aries; even signs start Capricorn. |
Remaining (D6, D8, D16, D20, D24, D60): generic calculate_varga(lon, n).
Critical: The existing
calculate_varga(lon, 4)does not match Parashari D4. Thechaturthamsha()wrapper must use the sign-offset formula, not the generic call.
Key Formulas
# D2 Hora
sign_idx = int(sidereal_lon // 30)
deg_in_sign = sidereal_lon % 30
half = 0 if deg_in_sign < 15 else 1
is_odd_sign = (sign_idx % 2 == 0) # 0-based: Aries=0 is 1st (odd) sign
if is_odd_sign:
hora_sign = 4 if half == 0 else 3 # Leo(4) or Cancer(3)
else:
hora_sign = 3 if half == 0 else 4 # Cancer(3) or Leo(4)
# D4 Chaturthamsha
segment_within_sign = int((sidereal_lon % 30) / 7.5) # 0β3
d4_sign = (sign_idx + segment_within_sign) % 12
# D27 Saptavimshamsha
TRIPLICITY_START = {"fire": 0, "earth": 3, "air": 6, "water": 9}
SIGN_ELEMENT = ["fire","earth","air","water"] * 3 # AriesβPisces
start = TRIPLICITY_START[SIGN_ELEMENT[sign_idx]]
segment = int((sidereal_lon % 30) / (30/27))
d27_sign = (start + segment) % 12
# D40 Khavedamsha
seg_40 = int((sidereal_lon % 30) / 0.75)
start_40 = 0 if (sign_idx % 2 == 0) else 6 # Aries or Libra
d40_sign = (start_40 + seg_40) % 12
# D45 Akshavedamsha
seg_45 = int((sidereal_lon % 30) / (30/45))
start_45 = 0 if (sign_idx % 2 == 0) else 9 # Aries or Capricorn
d45_sign = (start_45 + seg_45) % 12
Proposed Public Surface (additions to varga.py)
def hora(sidereal_longitude: float) -> VargaPoint: ... # D2
def chaturthamsha(sidereal_longitude: float) -> VargaPoint: ... # D4
def shashthamsha(sidereal_longitude: float) -> VargaPoint: ... # D6
def ashtamsha(sidereal_longitude: float) -> VargaPoint: ... # D8
def shodashamsha(sidereal_longitude: float) -> VargaPoint: ... # D16
def vimshamsha(sidereal_longitude: float) -> VargaPoint: ... # D20
def chaturvimshamsha(sidereal_longitude: float) -> VargaPoint: ... # D24
def saptavimshamsha(sidereal_longitude: float) -> VargaPoint: ... # D27
def khavedamsha(sidereal_longitude: float) -> VargaPoint: ... # D40
def akshavedamsha(sidereal_longitude: float) -> VargaPoint: ... # D45
def shashtiamsha(sidereal_longitude: float) -> VargaPoint: ... # D60
Tricky Edge Cases
sign_idx % 2 == 0means "odd sign" in 0-based indexing (Aries=0 is the 1st, which is odd). This is counterintuitive β document clearly.- D60 sign assignment is generic; the optional named Shashtiamsha lords (60 traditional names from BPHS) are future enrichment.
- Use
int(lon % 30 // (30/n))not rounding at segment boundaries.
Estimated Complexity
Moderate. Most wrappers are trivial; D2/D4/D27/D40/D45 require careful offset arithmetic and Jhora spot-checks.
System 2 β Vedic Planetary Dignities
Primary Authority
Parashara, BPHS Ch. 3, 26, 28. Kalyana Varma, Saravali Ch. 5β6.
Validation Reference
Jhora (dignity display + Panchadha Maitri grid); Raman, A Manual of Hindu Astrology.
File
moira/vedic_dignities.py
Exaltation and Debilitation
| Planet | Exaltation | Deepest degree | Debilitation |
|---|---|---|---|
| Sun | Aries | 10Β° | Libra |
| Moon | Taurus | 3Β° | Scorpio |
| Mars | Capricorn | 28Β° | Cancer |
| Mercury | Virgo | 15Β° | Pisces |
| Jupiter | Cancer | 5Β° | Capricorn |
| Venus | Pisces | 27Β° | Virgo |
| Saturn | Libra | 20Β° | Aries |
Mulatrikona Ranges
| Planet | Sign | Range |
|---|---|---|
| Sun | Leo | 0Β°β20Β° |
| Moon | Taurus | 3Β°β30Β° |
| Mars | Aries | 0Β°β12Β° |
| Mercury | Virgo | 15Β°β20Β° |
| Jupiter | Sagittarius | 0Β°β10Β° |
| Venus | Libra | 0Β°β15Β° |
| Saturn | Aquarius | 0Β°β20Β° |
Dignity Rank Order
Exaltation > Mulatrikona > Swakshetra > Friend's sign > Neutral's sign > Enemy's sign > Debilitation
Natural Friendship (Naisargika Maitri β BPHS Ch. 3)
| Planet | Friends | Neutral | Enemies |
|---|---|---|---|
| Sun | Moon, Mars, Jupiter | Mercury | Venus, Saturn |
| Moon | Sun, Mercury | Mars, Jupiter, Venus, Saturn | β |
| Mars | Sun, Moon, Jupiter | Venus, Saturn | Mercury |
| Mercury | Sun, Venus | Mars, Jupiter, Saturn | Moon |
| Jupiter | Sun, Moon, Mars | Saturn | Mercury, Venus |
| Venus | Mercury, Saturn | Mars, Jupiter, Moon | Sun |
| Saturn | Mercury, Venus | Jupiter | Sun, Moon, Mars |
Temporary Friendship Formula
distance = (sign_B - sign_A) % 12
- Distances {1,2,3,9,10,11}: temporary friend
- Distances {0,4,5,6,7,8}: temporary enemy
- Distance 0 (self): excluded
Panchadha Maitri (Compound Relationship)
| Natural + Temporary | Compound |
|---|---|
| Friend + Friend | Adhi Mitra (Great Friend) |
| Friend + Enemy | Sama (Neutral) |
| Neutral + Friend | Mitra (Friend) |
| Neutral + Enemy | Shatru (Enemy) |
| Enemy + Friend | Sama (Neutral) |
| Enemy + Enemy | Adhi Shatru (Great Enemy) |
Proposed Public Surface
EXALTATION_SIGN: dict[str, int]
EXALTATION_DEGREE: dict[str, float]
DEBILITATION_SIGN: dict[str, int]
MULATRIKONA_SIGN: dict[str, int]
MULATRIKONA_START: dict[str, float]
MULATRIKONA_END: dict[str, float]
OWN_SIGNS: dict[str, list[int]]
NATURAL_FRIENDS: dict[str, set[str]]
NATURAL_NEUTRALS: dict[str, set[str]]
NATURAL_ENEMIES: dict[str, set[str]]
class VedicDignityRank:
EXALTATION = "exaltation"
MULATRIKONA = "mulatrikona"
OWN_SIGN = "own_sign"
FRIEND_SIGN = "friend_sign"
NEUTRAL_SIGN = "neutral_sign"
ENEMY_SIGN = "enemy_sign"
DEBILITATION = "debilitation"
class CompoundRelationship:
GREAT_FRIEND = "adhi_mitra"
FRIEND = "mitra"
NEUTRAL = "sama"
ENEMY = "shatru"
GREAT_ENEMY = "adhi_shatru"
@dataclass(frozen=True, slots=True)
class VedicDignityResult:
planet: str
sidereal_longitude: float
sign_index: int
sign: str
dignity_rank: str
is_exalted: bool
is_debilitated: bool
is_mulatrikona: bool
is_own_sign: bool
exaltation_score: float # 0.0 at debilitation, 1.0 at deepest exaltation
@dataclass(frozen=True, slots=True)
class PlanetaryRelationship:
from_planet: str
to_planet: str
natural: str # "friend" / "neutral" / "enemy"
temporary: str # "friend" / "enemy"
compound: str # CompoundRelationship constant
def vedic_dignity(
planet: str,
sidereal_longitude: float,
) -> VedicDignityResult: ...
def planetary_relationships(
sidereal_longitudes: dict[str, float],
) -> list[PlanetaryRelationship]: ...
Tricky Edge Cases
- Mercury at Virgo 15Β° is at deepest exaltation AND at the start of its Mulatrikona range. Check exaltation before Mulatrikona in the cascade.
- Moon's Mulatrikona: Parashara gives 3Β°β30Β°; some commentators give 4Β°β30Β°. Use 3Β° (primary source), document variant.
- Scope: 7 classical planets only. Rahu and Ketu are not subjects of this system.
Estimated Complexity
Moderate.
System 3 β Jaimini Karakas
Primary Authority
Jaimini, Jaimini Sutras, Adhyaya 1, Pada 1, Sutras 1β14.
Validation Reference
Jhora (7/8-karaka mode); Sanjay Rath, Jaimini Maharishi's Upadesa Sutras (2002).
File
moira/jaimini.py
Core Algorithm
# 7-karaka scheme
planets_7 = ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"]
degrees = {p: sidereal_lon[p] % 30.0 for p in planets_7}
sorted_planets = sorted(planets_7, key=lambda p: degrees[p], reverse=True)
# sorted_planets[0] = Atmakaraka, [1] = Amatyakaraka, ..., [6] = Darakaraka
Rahu (8-karaka scheme only): degree_in_sign = 30 - (rahu_sidereal_lon % 30) β inverted because Rahu moves retrograde.
Karaka Sequences
| Rank | 7-karaka | 8-karaka |
|---|---|---|
| 1 | Atmakaraka (AK) | Atmakaraka (AK) |
| 2 | Amatyakaraka (AmK) | Amatyakaraka (AmK) |
| 3 | Bhratrikaraka (BK) | Bhratrikaraka (BK) |
| 4 | Matrikaraka (MaK) | Matrikaraka (MaK) |
| 5 | Pitrikaraka (PiK) | Pitrikaraka (PiK) |
| 6 | Gnatikaraka (GK) | Putrakaraka (PuK) |
| 7 | Darakaraka (DK) | Gnatikaraka (GK) |
| 8 | β | Darakaraka (DK) |
Proposed Public Surface
KARAKA_NAMES_7: list[str]
KARAKA_NAMES_8: list[str]
@dataclass(frozen=True, slots=True)
class KarakaAssignment:
karaka_name: str
karaka_rank: int # 1-based
planet: str
degree_in_sign: float # effective degree used for sorting
sidereal_longitude: float
is_rahu_inverted: bool
@dataclass(frozen=True, slots=True)
class JaiminiKarakaResult:
assignments: list[KarakaAssignment]
scheme: int # 7 or 8
atmakaraka: str # convenience accessor
tie_warnings: list[tuple[str, str]] # planet pairs with equal degrees
def jaimini_karakas(
sidereal_longitudes: dict[str, float],
scheme: int = 7,
) -> JaiminiKarakaResult: ...
def atmakaraka(
sidereal_longitudes: dict[str, float],
scheme: int = 7,
) -> str: ...
Tricky Edge Cases
- Ketu is always excluded, even in the 8-karaka scheme. Only Rahu is the 8th candidate.
- Ties: astronomically rare, but expose a
tie_warningand apply a deterministic tiebreaker (document as indeterminate). - Atmakaraka at 29Β°59': valid edge case β no special handling needed,
% 30arithmetic handles it correctly.
Estimated Complexity
Simple. Core is a single sort with one special case (Rahu inversion).
System 4 β Panchanga
Primary Authority
Parashara, BPHS (Muhurta chapters). Varahamihira, Brihat Samhita Ch. 98β104.
Validation Reference
Jhora, Kala software.
File
moira/panchanga.py
Five Elements
| Element | Formula | Span |
|---|---|---|
| Tithi | int((moon_sid - sun_sid) % 360 / 12) + 1 |
12Β° each, 30 total |
| Vara | int(jd + 1.5) % 7 β Sun, Moon, Mars, Mercury, Jupiter, Venus, Saturn |
1 day |
| Nakshatra | delegate to sidereal.nakshatra_of(moon_tropical_lon, jd) |
13.33Β° each |
| Yoga | int((sun_sid + moon_sid) % 360 / (360/27)) + 1 |
13.33Β° each, 27 total |
| Karana | half-Tithi (int(diff / 6)); 60 per month |
6Β° each |
Karana mapping: position 0 = Kimstughna (fixed); positions 1β56 cycle 7 movable Karanas; positions 57β59 = Shakuni, Chatushpada, Naga (fixed).
Each element should return degrees_elapsed and degrees_remaining to enable muhurta "time until next X" calculations.
Proposed Public Surface
TITHI_NAMES: list[str] # 30 names: Pratipada β¦ Amavasya
YOGA_NAMES: list[str] # 27 names: Vishkumbha β¦ Vaidhriti
KARANA_NAMES: list[str] # 11 names
VARA_LORDS: list[str] # 7 lords, Sunday-origin
@dataclass(frozen=True, slots=True)
class PanchangaElement:
name: str
index: int # 0-based
number: int # 1-based
degrees_elapsed: float
degrees_remaining: float
@dataclass(frozen=True, slots=True)
class PanchangaResult:
jd: float
tithi: PanchangaElement
vara: PanchangaElement
vara_lord: str
nakshatra: NakshatraPosition # from sidereal.nakshatra_of
yoga: PanchangaElement
karana: PanchangaElement
ayanamsa_system: str
def panchanga_at(
sun_tropical_lon: float,
moon_tropical_lon: float,
jd: float,
ayanamsa_system: str = Ayanamsa.LAHIRI,
) -> PanchangaResult: ...
Tricky Edge Cases
- Vara boundary: The Vedic day begins at sunrise, not midnight. The base implementation operates on the astronomical JD and documents this limitation; sunrise-corrected Vara is a future policy parameter.
- Yoga skipping: True fast Moon can skip a 13.33Β° Yoga window in a day. Use true positions (doctrine-consistent), note the skip possibility in the docstring.
- Karana table boundary: The fixed Karanas at Amavasya/Pratipada are asymmetrically placed. Verify against Jhora at month boundaries.
Estimated Complexity
Moderate. Arithmetic is simple; Karana fixed/movable boundary requires careful table construction and validation.
System 5 β Alternative Dasha Systems
Primary Authority
- Ashtottari: BPHS chapter 46, verses 17β23. Verses 17β22 govern allocation and balance; verse 23 adds an applicability statement.
- Yogini: BPHS chapter 46, verses 195β200.
- Kalachakra: BPHS (Kalachakra Dasha chapter); Sanjay Rath commentary.
Validation Reference
Jhora, Kala software. Kalachakra: Jhora is the primary cross-check.
File
moira/dasha_systems.py
Ashtottari (108-year cycle)
Sun:6, Moon:15, Mars:8, Mercury:17, Saturn:10, Jupiter:19, Rahu:12, Venus:21
Starting lord and balance use alternating four- and three-nakshatra groups beginning at Ardra. The 28-place count includes Abhijit; the exact Abhijit arc is an explicitly separate traditional boundary convention, not wording found in BPHS 46.17β22. The complete applicability statements require more than an Ascendant sign alone, so incomplete policy input must not be treated as proof.
Yogini (36-year cycle)
Mangala (Moon):1, Pingala (Sun):2, Dhanya (Jupiter):3, Bhramari (Mars):4,
Bhadrika (Mercury):5, Ulka (Saturn):6, Siddha (Venus):7, Sankata (Rahu):8
Starting Yogini: add three to the one-based birth-nakshatra number and reduce
to a one-through-eight remainder; equivalently, with a zero-based index,
(nakshatra_index + 3) % 8. No eligibility condition. Sub-periods remain
proportional in the current implementation.
Kalachakra
Navamsha-based, Savya/Apasavya traversal direction, variable cycle length. Defer to Phase 2 within this module.
Proposed Public Surface
@dataclass(frozen=True, slots=True)
class AlternateDashaPeriod:
system: str # "ashtottari" | "yogini" | "kalachakra"
level: int
lord: str
start_jd: float
end_jd: float
sub: list["AlternateDashaPeriod"]
@dataclass(frozen=True, slots=True)
class AshtottariPolicy:
year_basis: str = "julian_365.25"
ayanamsa_system: str = Ayanamsa.LAHIRI
bypass_eligibility: bool = False
lagna_sign_index: int | None = None # required if bypass_eligibility=False
@dataclass(frozen=True, slots=True)
class YoginiPolicy:
year_basis: str = "julian_365.25"
ayanamsa_system: str = Ayanamsa.LAHIRI
def ashtottari(
moon_tropical_lon: float,
natal_jd: float,
levels: int = 2,
policy: AshtottariPolicy | None = None,
) -> list[AlternateDashaPeriod]: ...
def yogini_dasha(
moon_tropical_lon: float,
natal_jd: float,
levels: int = 2,
policy: YoginiPolicy | None = None,
) -> list[AlternateDashaPeriod]: ...
Estimated Complexity
- Ashtottari: Simple (structurally identical to Vimshottari, different tables + one eligibility condition)
- Yogini: Simple (8 lords, proportional sub-periods, no eligibility)
- Kalachakra: Complex (Savya/Apasavya traversal, Navamsha-sign mapping, variable cycle) β Phase 2
System 6 β Ashtakavarga
Primary Authority
Parashara, BPHS Ashtakavarga Adhyaya. Vaidyanatha Dikshita, Jataka Parijata (14th c.).
Engineering Reference
B.V. Raman, Ashtakavarga System of Prediction (1981) β the canonical English-language reference with verified tables. Commit to Raman as the authoritative table source; document it explicitly.
Validation Reference
Jhora (Bhinnashtakavarga display), Kala software.
File
moira/ashtakavarga.py
Algorithm
for planet_P in seven_planets:
bhinna_AV[planet_P] = [0] * 12
for sign_i in range(12):
for ref in [*seven_planets, "Lagna"]:
distance = (sign_i - sign_index[ref]) % 12 + 1 # 1β12
if distance in REKHA_TABLES[planet_P][ref]:
bhinna_AV[planet_P][sign_i] += 1
sarva[sign_i] = sum(bhinna_AV[P][sign_i] for P in seven_planets)
The REKHA_TABLES are 7 Γ 8 fixed lookup tables β 672 individual rekha/no-rekha values that must be encoded verbatim from Raman's reference and verified sign-by-sign against Jhora.
Phase 1: Unreduced Bhinnashtakavarga + Sarvashtakavarga.
Phase 2: Trikona Shodhana and Ekadhipatya Shodhana (reductions).
Proposed Public Surface
REKHA_TABLES: dict[str, dict[str, frozenset[int]]] # 7 planets Γ 8 references
@dataclass(frozen=True, slots=True)
class BhinnashtakavargaResult:
planet: str
rekhas: tuple[int, ...] # 12 counts, 0=Ariesβ¦11=Pisces
total_rekhas: int # sum (always 0β56)
@dataclass(frozen=True, slots=True)
class AshtakavargaResult:
jd: float
ayanamsa_system: str
bhinnashtakavarga: dict[str, BhinnashtakavargaResult]
sarvashtakavarga: tuple[int, ...] # 12 aggregate counts
def bhinnashtakavarga(
planet: str,
sign_indices: dict[str, int], # planet/lagna β sign index (0β11)
) -> BhinnashtakavargaResult: ...
def ashtakavarga(
sidereal_longitudes: dict[str, float], # includes "Lagna" key
) -> AshtakavargaResult: ...
def transit_strength(
planet: str,
transit_sign_index: int,
bhinna: BhinnashtakavargaResult,
) -> int: ... # 0β8
Tricky Edge Cases
- Table variants: different BPHS editions have minor discrepancies. Commit to Raman (1981) and document.
- Lagna participates as a sign index only β no longitude precision needed.
- Rahu/Ketu: excluded from core system. Scope to 7 planets + Lagna. Document as future extension.
Estimated Complexity
Moderate. Algorithm is simple; table transcription is the primary failure mode.
System 7 β Shadbala
Primary Authority
Parashara, BPHS Shadbala Adhyaya.
Engineering Reference
B.V. Raman, Graha and Bhava Balas (1959) β fully worked example chart verifiable to 2 decimal places in Rupas. Use this as the primary test fixture.
Validation Reference
Jhora full Shadbala display.
File
moira/shadbala.py
Unit
Shashtiamsas (Sha). 60 Sha = 1 Rupa.
Six Components
1. Sthana Bala (Positional Strength) β 5 sub-components
(a) Uchcha Bala
dist = abs(sidereal_lon - exaltation_lon)
if dist > 180: dist = 360 - dist
uchcha_bala = (180 - dist) / 3.0 # Sha, max 60
(b) Saptavargaja Bala β dignity in each of D1, D2, D3, D7, D9, D12, D30
| Dignity in varga | Shashtiamsas |
|---|---|
| Exaltation | 20 |
| Own sign | 15 |
| Adhi Mitra (great friend) | 10 |
| Mitra (friend) | 7.5 |
| Sama (neutral) | 5 |
| Shatru (enemy) | 2.5 |
| Adhi Shatru (great enemy) | 1.25 |
| Debilitation | 0 |
Sum across 7 vargas. Max = 140 Sha.
(c) Ojayugmarasyamsa Bala β odd/even sign strength
Sun, Mars, Jupiter, Saturn: 15 Sha in odd D1/D9 signs. Moon, Venus: 15 Sha in even signs.
(d) Kendradi Bala β angular house
Kendra (1,4,7,10): 60 Sha. Panapara (2,5,8,11): 30 Sha. Apoklima (3,6,9,12): 15 Sha.
(e) Drekkana Bala
Male planets (Sun, Mars, Jupiter) in 1st drekkana: 15 Sha. Hermaphrodite (Mercury, Saturn) in 2nd: 15 Sha. Female (Moon, Venus) in 3rd: 15 Sha.
2. Dig Bala (Directional Strength)
Strong direction by planet:
Sun, Mars: 10th house cusp
Jupiter, Mercury: 1st house cusp (Ascendant)
Moon, Venus: 4th house cusp
Saturn: 7th house cusp
dist = abs(sidereal_lon - strong_cusp_lon)
if dist > 180: dist = 360 - dist
dig_bala = (180 - dist) / 3.0 # Sha, max 60
3. Kala Bala (Temporal Strength) β 6 sub-components
| Sub-component | Key |
|---|---|
| Nathonnatha | Day planets (Sun, Jupiter, Venus) max at noon; night planets (Moon, Mars, Saturn) max at midnight; Mercury always strong. |
| Paksha | Tithi 1β15: shukla = tithi. Tithi 16β30: shukla = 30 - tithi. Benefics: shukla * 4 Sha. Malefics: (15 - shukla) * 4 Sha. |
| Tribhaga | Jupiter: 1st third of day; Sun: 2nd; Saturn: 3rd. Moon: 1st third of night; Venus: 2nd; Mars: 3rd. Mercury: always. Value: 60 Sha in strong period, 0 otherwise. |
| Abda/Masa/Vara/Hora | Lord of current solar year, month, weekday, hour each receive 15 Sha. |
| Ayana | ayana_bala = 24 * sin(Sun_declination). sin(dec) = sin(obliquity) Γ sin(sun_lon). |
| Yuddha | Planets within 1Β° in same sign: victor (smaller longitude) gains loser's Yuddha Bala. |
4. Chesta Bala (Motional Strength)
Speed-based score vs mean speed. Retrograde = maximum (60 Sha). Sun and Moon: distance from apogee/perigee.
5. Naisargika Bala (Natural Fixed Strength β never changes)
| Planet | Sha |
|---|---|
| Sun | 60.00 |
| Moon | 51.43 |
| Venus | 42.85 |
| Jupiter | 34.28 |
| Mercury | 25.70 |
| Mars | 17.14 |
| Saturn | 8.57 |
6. Drig Bala (Aspectual Strength)
Sum weighted benefic aspects received; subtract weighted malefic aspects. Vedic aspect weights: full (7th sign) = 1.0; three-quarter (Mars: 4th/8th; Jupiter: 5th/9th; Saturn: 3rd/10th) = 0.75; half (5th/9th regular) = 0.5; quarter (3rd/10th regular) = 0.25.
Required Minimum Rupas
| Planet | Min Rupas |
|---|---|
| Sun | 6.5 |
| Moon | 6.0 |
| Mars | 5.0 |
| Mercury | 7.0 |
| Jupiter | 6.5 |
| Venus | 5.5 |
| Saturn | 5.0 |
Proposed Public Surface
@dataclass(frozen=True, slots=True)
class SthanaBala:
uchcha: float; saptavargaja: float; ojayugma: float
kendradi: float; drekkana: float; total: float
@dataclass(frozen=True, slots=True)
class KalaBala:
nathonnatha: float; paksha: float; tribhaga: float
abda_masa_vara_hora: float; ayana: float; yuddha: float; total: float
@dataclass(frozen=True, slots=True)
class PlanetShadbala:
planet: str
sthana_bala: SthanaBala
dig_bala: float
kala_bala: KalaBala
chesta_bala: float
naisargika_bala: float
drig_bala: float
total_shashtiamsas: float
total_rupas: float
required_rupas: float
is_sufficient: bool
@dataclass(frozen=True, slots=True)
class ShadbalaResult:
jd: float
ayanamsa_system: str
planets: dict[str, PlanetShadbala] # 7 planets
def shadbala(
sidereal_longitudes: dict[str, float],
planet_speeds: dict[str, float],
houses: "HouseCusps",
jd: float,
latitude: float,
longitude: float,
ayanamsa_system: str = Ayanamsa.LAHIRI,
) -> ShadbalaResult: ...
# Individual sub-component functions for testing:
def sthana_bala(planet: str, sidereal_lon: float, ...) -> SthanaBala: ...
def dig_bala(planet: str, sidereal_lon: float, houses: "HouseCusps") -> float: ...
def kala_bala(planet: str, jd: float, latitude: float, longitude: float, ...) -> KalaBala: ...
def chesta_bala(planet: str, speed: float, mean_speed: float) -> float: ...
def drig_bala(planet: str, sidereal_longitudes: dict[str, float]) -> float: ...
Tricky Edge Cases
- Saptavargaja requires completed varga.py (all 7 vargas, especially D2 Hora with Parashari rule). This is the hard dependency on System 1.
- Kala Bala requires sunrise/sunset β lat/lon input is mandatory. This makes Shadbala the only system with an indirect dependency on
moira.rise_set. - Ayana Bala declination:
sin(dec) = sin(obliquity) Γ sin(sun_lon). Usemoira.obliquity.mean_obliquity(jd). - Chesta Bala for luminaries: Sun uses distance from aphelion; Moon uses distance from perigee. Requires mean anomaly from
moira.planetsspeed data ormoira.nodes. - Yuddha threshold: Planetary war is restricted to same-sign conjunctions within 1Β°.
- Bhava Bala (house strength) is a related but separate system β keep out of scope.
Estimated Complexity
Complex. Highest sub-component count and most input dependencies of all 7 systems. Implement each sub-component as an isolated function with its own test fixture from Raman's Graha and Bhava Balas reference chart.
Consolidated Dependency Map
moira/sidereal.py β complete; provides tropical_to_sidereal, nakshatra_of, Ayanamsa
moira/varga.py β extend with 11 missing wrappers (D2/D4/D6/D8/D16/D20/D24/D27/D40/D45/D60)
moira/cycles.py β complete; weekday ruler available
moira/rise_set.py β complete; sunrise/sunset (required by Shadbala Kala Bala)
moira/panchanga.py β sidereal.py, cycles.py
moira/vedic_dignities.py β sidereal.py, constants.py
moira/jaimini.py β sidereal.py
moira/dasha_systems.py β sidereal.py (patterns from dasha.py)
moira/ashtakavarga.py β vedic_dignities.py (needed for Shodhana; not for base)
moira/shadbala.py β varga.py (all 7), vedic_dignities.py, panchanga.py,
ashtakavarga.py (optional Drig Bala variant),
rise_set.py, nodes.py (Moon apogee for Chesta Bala)
Authority Source Quick Reference
| System | Primary Canon | Engineering Reference |
|---|---|---|
| Shodashvarga | BPHS Ch. 6β22 | Jhora, Parashara Light 9.x |
| Vedic Dignities | BPHS Ch. 3, 26, 28; Saravali Ch. 5β6 | Raman, A Manual of Hindu Astrology |
| Jaimini Karakas | Jaimini Sutras, Adhyaya 1, Pada 1 | Sanjay Rath commentary; Jhora |
| Panchanga | BPHS Muhurta chapters; Brihat Samhita Ch. 98β104 | Jhora, Kala |
| Alt. Dashas | BPHS (Ashtottari/Kalachakra chapters); Rao, Yogini Dasha | Jhora, Kala |
| Ashtakavarga | BPHS Ashtakavarga Adhyaya; Jataka Parijata | Raman, Ashtakavarga System of Prediction (1981) |
| Shadbala | BPHS Shadbala Adhyaya | Raman, Graha and Bhava Balas (1959) |