Attachments forums

List of attachments posted on this forum.


All files on forums: 163497

Re: Python to MT5

Banzai, Fri Dec 26, 2025 4:05 pm

Cybernetic Oscillator

Python: June 2025
The Python code presented here is based on John Ehlers’ article in this issue, “Making A Better Oscillator,” which introduces his new cybernetic oscillator.

The routines given here perform tasks related to implementing the cybernetic oscillator using the Python language. This includes: importing the required python libraries; obtaining OHLCV data by using the Yahoo Finance Python package; using panda’s built-in plotting function to display a price chart; building blocks to build the cybernetic oscillator indicator; using MatPlotLib to plot close, CO1 and CO2 values; and finally, a callable routine to implement an example trading strategy based on rate-of-change, as described in Ehlers’ article.

An example chart plotting both a long-term and short-term cybernetic oscillator is shown in Figure 8. A chart illustrating the creation of an example rate-of-change strategy is shown in Figure 9. FIGURE 8: PYTHON. John Ehlers’ cybernetic oscillator is demonstrated here on a daily chart of the S&P 500. A short and longer-trend version are shown. FIGURE 9: PYTHON. An example trading strategy incorporating the cybernetic oscillator is created for demonstration purposes. The strategy is a simple dual ROC strategy. The indicators are visualized using the MatplotLib routine. Here, LP, BP1, and BP2 can be observed along with price.

Code: Select all

#
# import required python libraries
#
%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf
import math
import datetime as dt
print(yf.__version__)

#
# Use Yahoo Finance python package to obtain OHLCV data 
#
symbol = '^GSPC'
symbol = 'SPY'
ohlcv = yf.download(symbol, start="1995-01-01", end="2025-04-18", group_by="Ticker", auto_adjust=True)
ohlcv = ohlcv[symbol]

#
# Use pandas built in plot function to see simple price chart 
#
ax = ohlcv['Close'].plot(
    figsize=(9,6), 
    grid=True, 
    title=f'{symbol}', 
    #marker='.'
)


#
# Building block / routines used to implement cybernetic oscillaor indicator 
#
def calc_highpass(price, period):
    
    a1 = np.exp(-1.414 * np.pi / period)
    b1 = 2 * a1 * np.cos(math.radians(1.414 * 180 / period))
    c2 = b1
    c3 = -a1 * a1
    c1 = (1 + c2 - c3)/4

    out_values = []
    for i in range(len(price)):
        if i >= 4:
            out_values.append(
                c1*(price[i] - 2*price[i-1] + price[i-2]) + c2*out_values[i-1] + c3*out_values[i-2]
            )
        else:
            out_values.append(price[i])
    
    return out_values

def calc_super_smoother(price, period):
        
        a1 = np.exp(-1.414 * np.pi / period)
        b1 = 2 * a1 * np.cos(math.radians(1.414 * 180 / period))
        c2 = b1
        c3 = -a1 * a1
        c1 = (1 - c2 - c3)
    
        out_values = []
        for i in range(len(price)):
            if i >= 4:
                out_values.append(c1*(price[i]+price[i-1])/2 + c2*out_values[i-1] + c3*out_values[i-2])
            else:
                out_values.append(price[i])
        
        return out_values

def calc_rms(price):
    
    length = len(price)
    sum_sq = 0
    for count in range(length):
        sum_sq += price[count] * price[count]
    return np.sqrt(sum_sq / length)

def calc_cybernetic_oscillator(close, params=(30, 20)):

    hp_length = params[0]
    lp_length = params[1]
    
    df = pd.DataFrame(index=close.index, data=close)
    df['HP'] = calc_highpass(close, hp_length)
    df['LP'] = calc_super_smoother(df['HP'], lp_length)
    df['RMS'] = df['LP'].rolling(100).apply(calc_rms)
    df['CO'] = df['LP']/df['RMS']

    return df['CO']

#
# Exampe python code to create two versions of the cybernetic oscillator 
# as presented in June 2005 Trader Tip article
#

lp_length = 20
hp1_length = 30
hp2_length = 250

co1_params=(hp1_length, lp_length)
co2_params=(hp2_length, lp_length)

df = ohlcv.copy()
df['CO1'] = calc_cybernetic_oscillator(ohlcv['Close'], params=co1_params)
df['CO2'] = calc_cybernetic_oscillator(ohlcv['Close'], params=co2_params)
df


#
# MatPlotLib routine to plot Close, CO1 and CO2 values
#
def plot_indicators1(df):
    # Create a figure with three subplots stacked vertically
    fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(9, 6), sharex=True)

    # Plotting the first subplot (e.g., Price Data)
    ax1.set_title(f"S&P 500")
    ax1.plot(df.index, df['Close'], label='Close', color='blue', marker='.')
    ax1.grid(True, linestyle='--', alpha=0.5)
    ax1.set_ylabel('Price Plot')
    ax1.legend(loc='upper left', bbox_to_anchor=(1, 1))

    # Plotting the second subplot
    ax2.set_title(f'Cybernetic Oscillator {co1_params}')
    ax2.plot(df.index, df['CO1'], label='CO1', color='red')
    ax2.axhline(y=0, color='black', linestyle='-', label='zero')
    #ax2.set_ylabel('Linear Slope')
    ax2.grid(True, linestyle='-', alpha=0.5)
    ax2.legend(loc='upper left', bbox_to_anchor=(1, 1))

    # Plotting the third subplot
    ax3.set_title(f'Cybernetic Oscillator {co2_params}')
    ax3.plot(df.index, df['CO2'], label='CO2', color='blue')
    ax3.axhline(y=0, color='black', linestyle='-', label='zero')
    ax3.grid(True, linestyle='-', alpha=0.5)
    ax3.legend(loc='upper left', bbox_to_anchor=(1, 1))
    
    # Rotate x-axis labels for better readability
    plt.xticks(rotation=45)

    # Improve overall layout
    plt.tight_layout()

    # Show the plot
    plt.show()



plot_indicators1(df['2024-03':'2025'])


#
# Example python code to implement the simple dual ROC strategy showcased 
# in trader tip article the python code is bundled in a *strategy* routine 
# so it can easily be called, paramater as passed into the routine as a variable
#
def strategy(ohlcv, params):
    
    df = ohlcv.copy()
    df['LP'] = calc_super_smoother(df['Close'], params[0])
    df['BP1'] = calc_highpass(df['LP'],params[1] )
    df['ROC1'] = df['BP1'] - df['BP1'].shift(2)
    
    df['BP2'] = calc_highpass(df['LP'],params[2])
    df['ROC2'] = df['BP2'] - df['BP2'].shift(2)
    
    df['Signal'] = np.where((df['ROC1'] > 0 ) & (df['ROC2'] >0 ), 1, np.nan)
    df['Signal'] = np.where((df['ROC1'] < 0 ) & (df['ROC2'] <0 ), 0, df['Signal'])
    df['Signal'] = df['Signal'].fillna(method='ffill')
    return df


params=(20, 55, 156)
data = strategy(ohlcv, params)
data


#
# The Simple Dual ROC strategy indicators are visualized in the MatplotLib 
# routine below. Here LP, BP1 and BP2 can be see observed along with price
#
def plot_indicators2(df):
    
    # Create a figure with three subplots stacked vertically
    fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(9, 6), sharex=True)

    # Plotting the first subplot (e.g., Price Data)
    ax1.set_title(f"Ticker={symbol}")
    ax1.plot(df.index, df['Close'], label='Close', color='blue',)
    ax1.plot(df.index, df['LP'], label='LP', color='orange', )
    ax1.grid(True, linestyle='--', alpha=0.5)
    ax1.set_ylabel('Price Plot')
    ax1.legend(loc='upper left', bbox_to_anchor=(1, 1))

    # Plotting the second subplot
    ax2.set_title(f'BP1')
    ax2.plot(df.index, df['BP1'], label='BP1', color='red')
    ax2.axhline(y=0, color='black', linestyle='--', label='zero')
    #ax2.set_ylabel('Linear Slope')
    ax2.grid(True, linestyle='-', alpha=0.5)
    ax2.legend(loc='upper left', bbox_to_anchor=(1, 1))

    # Plotting the third subplot
    ax3.set_title(f'BP2')
    ax3.plot(df.index, df['BP2'], label='BP2', color='blue')
    ax3.axhline(y=0, color='black', linestyle='--', label='zero')
    ax3.grid(True, linestyle='-', alpha=0.5)
    ax3.legend(loc='upper left', bbox_to_anchor=(1, 1))
    
    # Rotate x-axis labels for better readability
    plt.xticks(rotation=45)

    # Improve overall layout
    plt.tight_layout()

    # Show the plot
    plt.show()

plot_indicators2(data['2024-03':'2025'])


#
# A simple backtest framework/routines are provided which allow changing 
# parameters and visualize performance results. Performance can be compared 
# against Buy&Hold of same ticker by setting. All buy and sell occur on the 
# day of the buy/sell signal using market close values
#
def backtest(data):

    df = data.copy()
    
    df['Return Ratio'] = np.where(df['Signal'].shift()==1, 1+df['Close'].pct_change(),1 )
    df['Strategy'] = df['Return Ratio'].cumprod()
    df['BH'] = (1+df['Close'].pct_change()).cumprod()

    df['Peak'] = df['Strategy'].cummax()
    df['% DD'] = 100*(df['Strategy']/df['Peak']-1)

    df['Peak'] = df['BH'].cummax()
    df['BH % DD'] = 100*(df['BH']/df['Peak']-1)

    df.at[df.index[0], 'Strategy'] = 1
    df.at[df.index[0], 'BH'] = 1

    return df


def plot_backtest_results(df, strategy_name="Simple Dual ROC Strategy", compare_bh_ena=False):

    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(9, 6), sharex=True)

    ax1.set_title(f"{strategy_name}, Ticker={symbol},")
    ax1.plot(df.index, df['Strategy'], label='Strategy Equity Curve', color='blue',)

    if compare_bh_ena:
        ax1.plot(df.index, df['BH'], label='Buy & Hold Equity Curve', color='orange',)
    ax1.grid(True, linestyle='--', alpha=0.5)
    ax1.set_ylabel('Cumulative Return')
    ax1.legend(loc='upper left', bbox_to_anchor=(1, 1))

    ax2.set_title(f"% Drawdown")
    ax2.plot(df.index, df['% DD'], label='Strategy Drawdown', color='blue',)

    if compare_bh_ena:
        ax2.plot(df.index, df['BH % DD'], label='Buy & Hold Drawdown', color='orange',)

    ax2.grid(True, linestyle='--', alpha=0.5)
    ax2.set_ylabel('% drawdown')
    ax2.legend(loc='upper left', bbox_to_anchor=(1, 1))

#
# Putting the building blocks toegther, parameters can be changed and results viewed visually
# Here using default values presenyed in article
#
params=(20, 55, 156)
data = strategy(ohlcv, params)
df = backtest(data['2009':'2024'])
plot_backtest_results(df, strategy_name="Simple Dual ROC Strategy", compare_bh_ena=False)


#
# Here change parameter and see results compared to buy & hold of same ticker
#
params=(50, 55, 156)
data = strategy(ohlcv, params)
df = backtest(data['2009':'2024'])
plot_backtest_results(df, compare_bh_ena=True)
All files in topic