Coding Fullmoon

In the Stocks & Commodities magazine, an EasyLanguage code was published that I would like to implement in AmiBroker version 6.10 or earlier. Since I have limited programming experience, I utilized AI assistance and received the following code from DeepSeek. Unfortunately, this code contains errors. Can someone help me resolve this issue?​

Thank you Martin

// Konvertierung von Julian Ephemeris Date zu Kalenderdatum
function TSMJulianEDtoDate(Jdate)
{
    local zz, fract, aa, alpha, bb, cc, dd, ee, daydec, mo, tyear, btemp, iday;
    
    zz = floor(Jdate + 0.5);
    fract = Jdate + 0.5 - zz;
    
    if(zz < 2299161) {
        aa = zz;
    } else {
        alpha = floor((zz - 1867216.25) / 36524.25);
        aa = zz + 1 + alpha - floor(alpha / 4);
    }
    
    bb = aa + 1524;
    btemp = (bb - 122.1) / 365.25;
    cc = floor(btemp);
    dd = floor(365.25 * cc);
    ee = floor((bb - dd) / 30.6001);
    daydec = bb - dd - floor(30.6001 * ee) + fract;
    
    mo = IIf(ee < 13.5, ee - 1, ee - 13);
    tyear = IIf(mo > 2.5, cc - 4716, cc - 4715);
    
    // Runde Tag auf ganze Zahl
    iday = round(daydec);
    
    return tyear * 10000 + mo * 100 + iday;
}

// Mondphasen-Berechnung
nfm = 1000;
n = -nfm/2;

k = TT = fullmoon = newmoon = 0;
newdate = fulldate = Null;

for(ix = 1; ix <= nfm; ix++)
{
    // Neue Monde
    k = 2000 + n;
    TT = k / 1236.85;
    fullmoon = 2451550.09765 + 29.530588853 * k + 
               0.0001337 * TT^2 - 0.000000150 * TT^3 + 
               0.00000000073 * TT^4;
    
    // Volle Monde
    k = 2000 + n + 0.5;
    TT = k / 1236.85;
    newmoon = 2451550.09765 + 29.530588853 * k + 
              0.0001337 * TT^2 - 0.000000150 * TT^3 + 
              0.00000000073 * TT^4;
    
    // Speichere Ergebnisse in Arrays
    fulldate[ix] = TSMJulianEDtoDate(fullmoon);
    newdate[ix] = TSMJulianEDtoDate(newmoon);
    
    n++;
}

// Ausgabe der Ergebnisse (Beispiel)
Plot(Close, "Price", colorBlack, styleCandle);
Title = "Mondphasen-Indikator\nNeumond: " + newdate[1] + "\nVollmond: " + fulldate[1];
1 Like

I found this code here, see link included at the top of the AFL. It seems to correspond to other charts. Also many python codes available:

// https://gist.github.com/endel/dfe6bb2fbe679781948c

function getMoonPhase( iyear, imonth, iday )
{
    cc = e = jd = b = 0;

    if( imonth < 3 )
    {
        iyear--;
        imonth += 12;
    }

    ++imonth;

    cc = 365.25 * iyear;
    e = 30.6 * imonth;
    jd = cc + e + iday - 694039.09; //jd is total days elapsed
    jd /= 29.5305882; //divide by the moon cycle
    b = Int( jd ); //int(jd) -> b, take integer part of jd
    jd -= b; //subtract integer part to leave fractional part of original jd

    b = round( jd * 8 ); //scale fraction from 0-8 and round

    if( b >= 8 )
    {
        b = 0; //0 and 8 are the same so turn 8 into 0
    }

    // 0 => New Moon
    // 1 => Waxing Crescent Moon
    // 2 => Quarter Moon
    // 3 => Waxing Gibbous Moon
    // 4 => Full Moon
    // 5 => Waning Gibbous Moon
    // 6 => Last Quarter Moon
    // 7 => Waning Crescent Moon

    return b;
}


bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );

SetChartBkColor( ColorRGB( 0, 0, 0 ) );
SetChartOptions( 0, chartShowArrows | chartShowDates );

Plot( C, "", colorWhite, styleCandle, Null, Null, 0, 0, 1 );

yy = Year();
mm = Month();
dd = Day();

GfxSetZOrder( -5 );
GfxSetCoordsMode( 1 );

for( i = fvb; i <= lvb; i++ )
{
    pp = getMoonPhase( yy[i], mm[i], dd[i] );

    if( pp == 4 )
    {
        GfxSelectPen( colorYellow, 1 );
        GfxSelectSolidBrush( colorYellow );
        GfxCircle( i, H[i] + ( H[i] / 100 * 0.3 ), 1 );
        //PlotTextsetfont("\u25CF", "Symbola", 40, i, H[i], colorYellow, colorDefault, 30 );

    }

    if( pp == 0 )
    {
        GfxSelectPen( colorYellow, 1 );
        GfxSelectSolidBrush( colorBlack );
        GfxCircle( i, H[i] + ( H[i] / 100 * 0.3 ), 1 );
        //PlotTextsetfont("\uFF2F", "Symbola", 20, i, H[i], colorYellow, colorDefault, 10 );
    }
}


2 Likes

Thank you very much - I will have a look!

I also made a version using Python. The top chart shows code above and bottom chart is the Python version. Top chart gives a range. Bottom chart I tried to approach the max and min. Today (the 13-th) it is actually not yet the top. Full moon is tomorrow

AFL CODE:

PyLoadFromFile( "lunaR", "C:\\Users\\win 10\\AppData\\Local\\Programs\\Python\\Python312\\mypython\\lunar\\lunar_PY.py" );

bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );

SetChartBkColor( ColorRGB( 0, 0, 0 ) );
SetChartOptions( 0, chartShowArrows | chartShowDates );

Plot( C, "", colorWhite, styleCandle, Null, Null, 0, 0, 1 );

yyyy = Year();
mm = Month();
dd = Day();

GfxSetZOrder( -5 );
GfxSetCoordsMode( 1 );

aa = Null;

for( i = fvb; i <= lvb; i++ )
{
    aa[i] = PyEvalFunction( "lunaR", "getLunarPhase", NumToStr( yyyy[i], 1.0 ), NumToStr( mm[i], 1.0 ), NumToStr( dd[i], 1.0 ) );
}

tops = pk = aa > Ref( HHV( aa, 1 ), -1 ) AND Ref( HHV( aa, 1 ), 1 ) <= aa AND aa > 0.97;
bots = tr = aa < Ref( LLV( aa, 1 ), -1 ) AND Ref( LLV( aa, 1 ), 1 ) >= aa AND aa < 0.03;

Plot( aa, "", colorAqua, styleLine | styleOwnScale, Null, Null, 0, 0, 1 );

for( i = fvb; i <= lvb; i++ )
{
    if( tops[i] )
    {
        GfxSelectPen( colorYellow, 1 );
        GfxSelectSolidBrush( colorYellow );
        GfxCircle( i, H[i] + ( H[i] / 100 * 0.3 ), 1 );
    }

    if( bots[i] )
    {
        GfxSelectPen( ColorRGB( 120, 120, 0 ), 1 );
        GfxSelectSolidBrush( ColorRGB( 40, 40, 0 ) );
        GfxCircle( i, L[i] - ( L[i] / 100 * 0.3 ), 1 );
    }
}

PYTHON CODE:

# pip install ephem

import ephem
observer = ephem.Observer()
# NY city
observer.lat = '40.730610'
observer.long = '-73.935242'

def getLunarPhase( yyyy, mm, dd ):

    yyyy = str(yyyy)
    mm = str(mm)
    dd = str(dd)

    strdate1 = yyyy + '/' + mm + '/' + dd 
    # Define the date
    observer.date = strdate1

    # Define the moon
    moon = ephem.Moon()

    # Calculate the phase of the moon
    moon.compute(observer)

    # Print the phase of the moon
    # print(moon.moon_phase)
    return moon.moon_phase
1 Like

Thanks
Hello is it possible to send you the article via E-Mail?

well I am no expert on this. Just like to play around with it. If you learn how to use Python in Amibroker that is quite useful for these type of things since there are so many libraries that you can use made by people who know what they are doing.

You should not be posting formulas that use 3rd party plugins without instructions how to get the plugin. Also in the presence of AFL implementation using plugins make no sense

yes I forgot to add

 import AmiPy

you need to add that in the Python file (at the top). It worked for me because I had that file already loaded elsewhere

@masiegert here is a full AFL translation.

I left the _TRACEF() lines so you can further debug it if needed.

The calculation of the 1000 dates is done once and stored in a Matrix (you can force to redo it using the reset "Trigger").
The conversion from "datenum" to "datetime" sometimes result in invalid dates (I added some extra validation code, but as commented in the code, probably this is not the best way do handle it).

// https://forum.amibroker.com/t/coding-fullmoon/39820

// Port from EL code published in the 2025 Bonus edition
// of the Technical Analysis of STOCKS & COMMODITIES
// See the article titled: "New Moon--Full Moon" by P.J.Kaufman

SetBarsRequired( -2 );
reset = ParamTrigger( "Recalculate", "Go" );

dt = DateTime();
dn = DateNum();
bi = BarIndex();

if( reset )
{
    StaticVarSet( "mp_Calculated", 0 );
}

function isLeapYear( y )
{
    return ( ( y % 4 == 0 ) AND( y % 100 != 0 ) ) OR( y % 400 == 0 );
}

function getLastDayOfMonth( m, y )
{
    switch( m )
    {
        case  2: // Feb
            if( isLeapYear( y ) )
                days = 29;
            else
                days = 28;

            break;

        case  4: // Apr
        case  6: // Jun
        case  9: // Sep
        case 11: // Nov
            days = 30;
            break;

        default: // Jan, Mar, May, Jul, Aug, Oct, Dec
            days = 31;
            break;
    }

    return days;
}


function TSMJulianEDtoDate( Jdate )
{
    // TSMJulianEDtoDate : Convert Julian Ephemeris Date to Calendar Date
    // Copyright 1999, 2025 P.J.Kaufman. All rights reserved.

    local zz, fract, aa, alpha, bb, cc, dd, ee, daydec, mo, tyear, btemp, iday;

    zz = int( Jdate + .5 );
    fract = Jdate + .5 - zz;

    if( zz < 2299161 )
        aa = zz;
    else
    {
        alpha = int( ( zz - 1867216.25 ) / 36524.25 );
        aa = zz + 1 + alpha - int( alpha / 4 );
    }

    bb = aa + 1524;
    btemp = ( bb - 122.1 ) / 365.25;
    cc = int( btemp );
    dd = int( 365.25 * cc );
    ee = int( ( bb - dd ) / 30.6001 );
    daydec = bb - dd - int( 30.6001 * ee ) + fract;

    if( ee < 13.5 )
        mo = ee - 1;
    else
        mo = ee - 13;

    if( mo > 2.5 )
        tyear = cc - 4716;
    else
        tyear = cc - 4715;

    // Must round day due to TS decimal accuracy }
    iday = round( daydec );


    if( iDay <= 0 )
        iDay = 1;

    // TODO: validation for all months including Feb 29....
    // MAYBE THE WRONG APPROACH - probably it is better to add a day and set to the follwing month....
    ldm = getLastDayOfMonth( mo, tYear );

    if( iDay > ldm )
        iDay = ldm;

    if( tYear >= 2000 )
        abYear = 100 + tYear - 2000;
    else
        abYear = tYear - 1900;

    retValue = abyear * 10000 + mo * 100 + iday;

    // _TRACEF( "Jdate: %10.7f - Year: %g - Month: %g - Day: %g - Ret: %10.0f", jDate, tYear, Mo, iDay, retValue );
    return ( floor( retValue ) );
}

function dn2s( i, date_num )
{
    local str;

    str = DateTimeToStr( DateTimeConvert( 2, date_num ) );

    if( str == "Invalid DateTime" )
    {
        str = StrFormat( "%f - Invalid Date: %10.7f", i, date_num );

    }

    return str;
}

procedure SetArray( ix, array, dt_ )
{
    local index;

    index = Lookup( bi, dt_, 1 ); // find the next trading day....

    if( isFinite( index ) AND index > 0 )
    {
        array[index] = dt_;
        // _TRACEF( "%g - Setting bar %g - (%s)", ix, index, DateTimeToStr( dt_ ) );
    }
}

nfm = 1000;
n = -nfm / 2;
// do it once...
mpCalculated = Nz( StaticVarGet( "mp_Calculated", 0 ) );

if( not mpCalculated )
{
    moonDatesMatrix = Matrix( 2, 1000, 0 );

    for( ix = 1; ix <= nfm; ix++ )
    {
        // new Moons relative to the year 2000
        k = n;
        TT = k / 1236.85; // time in Julian centuries
        fullMoon = 2451550.09765 + 29.530588853 * k + 0.0001337 * TT ^ 2 - 0.000000150 * TT ^ 3 + 0.00000000073 * TT ^ 4;
        // full Moons relative to the year 2000 }
        k = n + .5;
        TT = k / 1236.85;
        newMoon = 2451550.09765 + 29.530588853 * k + 0.0001337 * TT ^ 2 - 0.000000150 * TT ^ 3 + 0.00000000073 * TT ^ 4;
        fullMoonDateNum = TSMJulianEDtoDate( fullMoon );
        newMoonDateNum = TSMJulianEDtoDate( newMoon );
        moonDatesMatrix[0][ix - 1] = DateTimeConvert( 2, fullMoonDateNum );
        moonDatesMatrix[1][ix - 1] = DateTimeConvert( 2, newMoonDateNum );
        // _TRACEF( "Full moon date %g - %10.7f - JDate: %s", ix, fullMoon, dn2s(ix, fullMoonDateNum ) );
        //_TRACEF( "New moon date %g - %10.7f - JDate: %s", ix, newMoon, dn2s( ix, newMoonDateNum ) );
        n = n + 1;
    }

    StaticVarSet( "mp_Calculated", 1 );
    StaticVarSet( "mp_moonDatesMatrix", moonDatesMatrix );

}

moonDatesMatrix = StaticVarGet( "mp_moonDatesMatrix" );
fmArray = null;
nmArray = null;

if( typeof( moonDatesMatrix ) == "matrix" )
{
    for( ix = 1; ix <= nfm; ix++ )
    {
        fullMoonDate = moonDatesMatrix[0][ix - 1];
        // _TRACEF("ix: %g - fullMoonDate: %s", ix, DateTimeToStr(fullMoonDate));
        newMoonDate = moonDatesMatrix[1][ix - 1];
        // _TRACEF("ix: %g - nwMoonDate: %s", ix, DateTimeToStr(newMoonDate));
        SetArray( ix, &fmArray, fullMoonDate );
        SetArray( ix, &nmArray, newMoonDate );
    }
}

GraphXSpace = 10;
maxY = HighestVisibleValue( H ) * 1.01;
Plot( Close, "Price", colorBlack, styleCandle | styleNoTitle );
PlotShapes( iif( isFinite( fmArray ), shapeCircle, null ), colorGold, 0, maxY );
PlotShapes( iif( isFinite( nmArray ), shapeSmallCircle, null ), colorLightOrange, 0, maxY );

_N( Title = StrFormat( "{{NAME}} - " + FullName() +
                       " - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) Vol " +
                      WriteVal( V, 1.0 ) + " {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ) );
dtSelected = SelectedValue( dt );
fmSelected = SelectedValue( fmArray );

if( fmSelected )
    Title = Title + " - Full Moon on " + WriteIf( fmSelected != dtSelected, EncodeColor( colorRed ), "" ) + DateTimeToStr( fmSelected );

nmSelected = SelectedValue( nmArray );

if( nmSelected )
    Title = Title + " - New Moon on " + WriteIf( nmSelected != dtSelected, EncodeColor( colorRed ), "" )  + DateTimeToStr( nmSelected );

Filter = 1;
AddColumn( fmArray, "Full Moon Array", formatDateTimeISO );
AddColumn( nmArray, "New Moon Array",  formatDateTimeISO );

The red text in the title means that the "shape" is not on the calculated "moon" day, but on the following trading day

I imagine there is room for improvement (and/or corrections), but I'll leave that up to you or other users who are interested in the phases of the moon!

1 Like

Thanks but have look please

@masigert, sorry, I forgot to check if it works for you who are still using version 6.10.

Probably "passing arguments by reference" was added after that version. I do not have it to test, but I hope this will work:

  1. Delete the SetArray() procedure (non longer needed).
  2. Replace this part of the previous code:
moonDatesMatrix = StaticVarGet( "mp_moonDatesMatrix" );
fmArray = null;
nmArray = null;

if( typeof( moonDatesMatrix ) == "matrix" )
{
    for( ix = 1; ix <= nfm; ix++ )
    {
        fullMoonDate = moonDatesMatrix[0][ix - 1];
        index = Lookup( bi, fullMoonDate, 1 ); // find the next trading day....

        if( isFinite( index ) AND index > 0 )
            fmArray[index] = fullMoonDate;

        newMoonDate = moonDatesMatrix[1][ix - 1];
        index = Lookup( bi, newMoonDate, 1 ); // find the next trading day....

        if( isFinite( index ) AND index > 0 )
            nmArray[index] = newMoonDate;
    }
}

// etc...

I don't think that passing values by reference was supported until AmiBroker v6.26. See: AmiBroker 6.26.0 BETA Read Me

If you're actively using AmiBroker, you should just purchase an upgrade license. While not strictly required, it makes sense to support @Tomasz and his continuing efforts.

4 Likes

1 more Python version that calculates the exact dates. It can calculate it to the second but I only extract the day. Only problem is that sometimes the fullmoon/newmoon fall inside a weekend or on a holiday. This is why I added a toggle to display them at the exact date, or at the closest date within 2 days.

I used Beppes display. So for today it looks like:

code below. I checked if the dates are correct with tables on the internet and it is correct. If you need help with the code let me know.

AFL code:

// exact dat or closest date within dp days. This because some dates are holidays or weekends
dateprox = ParamToggle( "Date Proximity", "Exact Date|Closest Date", 1 );
dp = 2;// number of days the fullmoon or newmoon date can deviate from date in the database

PyLoadFromFile( "lunaR4", "C:\\Users\\win 10\\AppData\\Local\\Programs\\Python\\Python312\\mypython\\lunar\\lunar4_PY.py" );

function calcDatenum( yy1, mm1, dd1 )
{
    dn = 10000 * ( yy1 - 1900 ) + 100 * mm1 + dd1;
    return dn;
}

bi = BarIndex();
fvb = FirstVisibleValue( bi );
lvb = LastVisibleValue( bi );
dn = DateNum();

SetChartBkColor( ColorRGB( 0, 0, 0 ) );
SetChartOptions( 0, chartShowArrows | chartShowDates );

Plot( C, "Price", colorWhite, styleCandle, Null, Null, 0, 0, 1 );

yyyy = Year();
mm = Month();
dd = Day();

GfxSetZOrder( -5 );
GfxSetCoordsMode( 1 );

fullmoon = 0;
newmoon = 0;

for( tt = yyyy[fvb]; tt <= yyyy[lvb]; tt++ )
{
    _N( vv = PyEvalFunction( "lunaR4", "get_moons_in_year", tt ) );
    _N( vv = StrReplace( vv, "[", "" ) );
    _N( vv = StrReplace( vv, "]", "" ) );

    for( i = 0; ( ss = StrExtract( vv, i ) ) != ""; i++ )
    {
        moontype = StrMid( ss, 1, 1 );

        if( moontype == "f" )
        {
            y1 = StrToNum( StrMid( ss, 2, 4 ) );
            m1 = StrToNum( StrMid( ss, 7, 2 ) );
            d1 = StrToNum( StrMid( ss, 10, 2 ) );

            // calculate datenum
            dn1 = calcDatenum( y1, m1, d1 );

            if( dateprox )
            {
                aa = diffdn = abs( dn - dn1 );
                tr = aa < Ref( LLV( aa, 1 ), -1 ) AND Ref( LLV( aa, 1 ), 1 ) >= aa;
                tr = IIf( tr AND aa <= dp, tr, 0 );
                fullmoon = IIf( tr, 1, fullmoon );
            }
            else
            {
                tr = dn == dn1;
                tr = IIf( tr, tr, 0 );
                fullmoon = IIf( tr, 1, fullmoon );
            }
        }

        if( moontype == "n" )
        {
            y1 = StrToNum( StrMid( ss, 2, 4 ) );
            m1 = StrToNum( StrMid( ss, 7, 2 ) );
            d1 = StrToNum( StrMid( ss, 10, 2 ) );

            // calculate datenum
            dn1 = calcDatenum( y1, m1, d1 );

            if( dateprox )
            {
                aa = diffdn = abs( dn - dn1 );
                tr = aa < Ref( LLV( aa, 1 ), -1 ) AND Ref( LLV( aa, 1 ), 1 ) >= aa;
                tr = IIf( tr AND aa <= dp, tr, 0 );
                newmoon = IIf( tr, 1, newmoon );
            }
            else
            {
                tr = dn == dn1;
                tr = IIf( tr, tr, 0 );
                newmoon = IIf( tr, 1, newmoon );
            }
        }
    }
}

maxY = HighestVisibleValue( H );
PlotShapes( iif( fullmoon, shapeCircle, null ), colorYellow, 0, maxY );
PlotShapes( iif( newmoon, shapeCircle, null ), ColorRGB( 60, 60, 0 ), 0, maxY );
PlotShapes( iif( newmoon, shapeHollowCircle, null ), ColorRGB( 80, 80, 0 ), 0, maxY );

PYTHON code

# pip install ephem

import ephem
import AmiPy 
import datetime

def get_moons_in_year(year):
    year= int(year)
    '''
    Returns a list of the full and new moons in a year. The list contains tuples
    of either the form (DATE,'full') or the form (DATE,'new')
    '''
    moons = []
    date = ephem.Date(datetime.date(year, 1, 1))
    while date.datetime().year == year:
        date = ephem.next_full_moon(date)
        date1 = ephem.localtime(date)
        date = ephem.next_new_moon(date)
        date2 = ephem.localtime(date)
        #moons.append(['{0:%a, %d %b %H:%M}'.format(date1),'{0:%a, %d %b %H:%M}'.format(date2)])
        moons.append(['{0:f%Y-%m-%d}'.format(date1),'{0:n%Y-%m-%d}'.format(date2)])
    '''
    Note that previous_first_quarter_moon() and previous_last_quarter_moon()
    are also methods
    '''
    return str(moons).replace(" ", "")

@Tomasz, while experimenting with this code I saw that certain dates in the future were not properly converted from datenum() to datetime() reverting to the previous century (uncomment and check the _TRACEF() lines) .

Today I did a further check in the function were dates are converted and got this error:

image

Is this something that will be addressed in a future release? Thanks

See a discussion here, I called it DT32, 32bit timestamp is limited by bits.

1 Like

This thread and Pi-Day inspired me to dust off my own solution to this quest from - forgive me the pun - many moons ago. After transcribing it to AFL it looks like this:

_SECTION_BEGIN("Moonphase");
  Pi = 3.14159265359;
  daysPerOrbit = 29.53058770576;// number of days in synodic month
  newMoonDate = StrToDateTime("2022-10-25");// a date of a new moon
  days = DateTimeDiff(DateTime(),newMoonDate) / (24 * 60 * 60);// DateTimeDiff returns seconds
  radians = (days / daysPerOrbit) * 2*Pi;
  phase = (1 - cos(radians)) / 2;// enforce values between 0 and 1
  Plot(phase,_DEFAULT_NAME(),ParamColor("Color",colorGold),styleDots|styleNoLine);
_SECTION_END();

1 Like

Thanks great Work!

found some other code from 2006 I believe. I added the code from Fritz inside it also.

Last date it does not catch. Since 3/14 was also full moon. I used the original code except for the plotting

original code:
https://www.mail-archive.com/amibroker@yahoogroups.com/msg06094.html

/* Full/New Moon signals indicator MkVII
   Accurate to within 1 Minute per 69 years.

  ©Copyright 2001-2006 Jose Silva
   for personal use only
   http://www.metastocktools.com

   Plots +1 Signal on Full Moon; -1 on New Moon.
   if FM/NM falls on a holiday OR weekend,
    Signal is plotted on the next trading Day.
   Warning: will NOT Plot last FM/NM Signal
    until it is confirmed on next trade Day.*/

// Input your local time zone
TZ = Param( "Your local Time Zone?  [-12 to +12hrs]", 1, -12, 12, 1 );

// Lunar cycle
LunarMonth = 29.530589;
Offset = 7.254621;

// Calendar }
leap = frac( Year() / 4 ) == 0 AND frac( Year() / 100 ) != 0 OR frac( Year() / 400 )
       == 0;
y = Year() * 365 + int( Year() / 4 ) - int( Year() / 100 ) + int( Year() / 400 );
m =
    IIf( Month() == 2, 31 - leap,
         IIf( Month() == 3, 59,
              IIf( Month() == 4, 90,
                   IIf( Month() == 5, 120,
                        IIf( Month() == 6, 151,
                             IIf( Month() == 7, 181,
                                  IIf( Month() == 8, 212,
                                       IIf( Month() == 9, 243,
                                               IIf( Month() == 10, 273,
                                                       IIf( Month() == 11, 304,
                                                               IIf( Month() == 12, 334, -leap ) ) ) ) ) ) ) ) ) ) );
CurrentDay = y + m + Day() - TZ / 24 - Offset;

// Full Moon }
FM = frac( CurrentDay / LunarMonth );
FM = PeakBars( FM, 1, 1 ) == 0;

// New Moon }
NM = frac( ( CurrentDay + LunarMonth / 2 ) / LunarMonth );
NM = PeakBars( NM, 1, 1 ) == 0;

//Plot Moon signals in own window }
//Plot( fm - nm, "Moon", colorWhite, styleHistogram );
//PlotShapes( IIf( ( FM - NM ) > 0, shapeCircle, IIf( ( FM - NM ) < 0, shapeHollowCircle, shapeNone ) ), colorBlack, 0, 0 );

SetChartBkColor( ColorRGB( 0, 0, 0 ) );
SetChartOptions( 0, chartShowArrows | chartShowDates );
Plot( C, "Price", colorWhite, styleCandle, Null, Null, 0, 0, 1 );

maxY = HighestVisibleValue( H );
PlotShapes( iif( FM, shapeCircle, null ), colorYellow, 0, maxY );
Plot( IIf( FM, 1, Null ), "", ColorRGB( 60, 60, 0 ), styleHistogram | styleOwnScale | styleNoLabel | styleDashed, 0, 1, 0, 0, 1 );

PlotShapes( iif( NM, shapeCircle, null ), ColorRGB( 60, 60, 0 ), 0, maxY );
PlotShapes( iif( NM, shapeHollowCircle, null ), ColorRGB( 80, 80, 0 ), 0, maxY );
Plot( IIf( NM, 1, Null ), "", ColorRGB( 60, 60, 0 ), styleHistogram | styleOwnScale | styleNoLabel | styleDashed, 0, 1, 0, 0, 1 );

// fritz
Pi = 3.14159265359;
daysPerOrbit = 29.53058770576;// number of days in synodic month
newMoonDate = StrToDateTime( "2022-10-25" ); // a date of a new moon
days = DateTimeDiff( DateTime(), newMoonDate ) / ( 24 * 60 * 60 ); // DateTimeDiff returns seconds
radians = ( days / daysPerOrbit ) * 2 * Pi;
phase = ( 1 - cos( radians ) ) / 2; // enforce values between 0 and 1
Plot( phase, _DEFAULT_NAME(), ParamColor( "Color", colorGold ), styleOwnScale | styleDashed );

by the way the top chart is the Python code I last posted