Mechanical strategy meet AI

I was bored but also am interested in the idea someone mentioned ( @Your_Full_Name on the @cubanpete_the_swiss thread?) of having a mechanical strategy of swiss dividend companies.

So I though “why not asking AI to write down a starting point?”, and after a few iterations here is the result (Note: I could have told it to download the CSV automatically but it failed a couple of times so I thought to start with the file already there. You can get the CSV here: https://www.ishares.com/ch/individual/en/products/264108/ishares-swiss-dividend-ch-fund )

Next steps? I didn’t really check the strategies there other than seeing that the ranking isn’t that well made.

import pandas as pd
import yfinance as yf
import os

# ---------------------------------------------
# Global variables
# ---------------------------------------------
HOLDINGS_FILE = "CHDVD_holdings.csv"
FACTORS_FILE = "CHDVD_with_factors.csv"
skip_tickers = {"CHF", "F-GSI", "GBP","SMZ5"}

factor_columns = [
    "dividend_yield", "pe_ratio", "pb_ratio", "market_cap",
    "roe", "roa", "profit_margin", "debt_to_equity"
]

# ---------------------------------------------
# Function to create CHDVD_with_factors.csv
# ---------------------------------------------
def create_factors_file():
    if not os.path.exists(HOLDINGS_FILE):
        print(f"⚠ {HOLDINGS_FILE} not found. Please download and prepare it first.")
        return None

    df = pd.read_csv(HOLDINGS_FILE)
    ticker_col = df.columns[0]
    # Assume the second column is company name
    name_col = df.columns[1] if len(df.columns) > 1 else None

    # Remove ignored tickers
    df = df[~df[ticker_col].isin(skip_tickers)].reset_index(drop=True)

    # Add Yahoo Finance ticker column
    df["yf_ticker"] = df[ticker_col].astype(str) + ".SW"

    for col in factor_columns:
        df[col] = None

    print("\nFetching factor data from yfinance...\n")
    for idx, row in df.iterrows():
        yf_ticker = row["yf_ticker"]
        try:
            info = yf.Ticker(yf_ticker).info
            df.loc[idx, "dividend_yield"] = info.get("dividendYield")
            df.loc[idx, "pe_ratio"] = info.get("trailingPE")
            df.loc[idx, "pb_ratio"] = info.get("priceToBook")
            df.loc[idx, "market_cap"] = info.get("marketCap")
            df.loc[idx, "roe"] = info.get("returnOnEquity")
            df.loc[idx, "roa"] = info.get("returnOnAssets")
            df.loc[idx, "profit_margin"] = info.get("profitMargins")
            df.loc[idx, "debt_to_equity"] = info.get("debtToEquity")
            print(f"✔ Retrieved data for {yf_ticker} ({row[name_col]})")
        except Exception as e:
            print(f"⚠ Failed for {yf_ticker} ({row[name_col]}): {e}")

    df.to_csv(FACTORS_FILE, index=False)
    print(f"\nSaved enriched data to: {FACTORS_FILE}\n")
    return df

# ---------------------------------------------
# Functions for scoring
# ---------------------------------------------
def dividend_scoring(df):
    name_col = df.columns[1]
    df["score_dividend"] = df["dividend_yield"].rank(ascending=False)
    print(df[[df.columns[0], name_col, "dividend_yield", "score_dividend"]])
    return df

def value_scoring(df):
    name_col = df.columns[1]
    df["score_value"] = df["pe_ratio"].rank(ascending=True) + df["pb_ratio"].rank(ascending=True)
    print(df[[df.columns[0], name_col, "pe_ratio", "pb_ratio", "score_value"]])
    return df

def quality_scoring(df):
    name_col = df.columns[1]
    df["score_quality"] = df["roe"].rank(ascending=False) + df["roa"].rank(ascending=False) + df["profit_margin"].rank(ascending=False) + df["debt_to_equity"].rank(ascending=True)
    print(df[[df.columns[0], name_col, "roe", "roa", "profit_margin", "debt_to_equity", "score_quality"]])
    return df

def momentum_scoring(df):
    name_col = df.columns[1]
    print("Momentum scoring not implemented yet (requires historical prices).")
    return df

def risk_scoring(df):
    name_col = df.columns[1]
    print("Risk scoring not implemented yet (requires historical prices).")
    return df

def composite_scoring(df):
    name_col = df.columns[1]
    for col in ["score_dividend", "score_value", "score_quality"]:
        if col not in df.columns:
            print(f"⚠ {col} not found, run the individual scoring first.")
            return df
    df["score_composite"] = df["score_dividend"] + df["score_value"] + df["score_quality"]
    print(df[[df.columns[0], name_col, "score_composite"]].sort_values("score_composite"))
    return df

# ---------------------------------------------
# Menu
# ---------------------------------------------
def menu():
    while True:
        print("\n==== CHDVD Scoring Menu ====")
        if not os.path.exists(HOLDINGS_FILE):
            print(f"⚠ {HOLDINGS_FILE} not found. Please download and prepare it first.")
        else:
            print(f"{HOLDINGS_FILE} found ✅")

        if os.path.exists(FACTORS_FILE):
            print(f"{FACTORS_FILE} found ✅")
            df = pd.read_csv(FACTORS_FILE)
        else:
            print(f"{FACTORS_FILE} not found. Press 1 to create it.")
            df = None

        print("\nMenu:")
        print("1 - Create CHDVD_with_factors.csv (fetch factors)")
        print("2 - Dividend scoring")
        print("3 - Value scoring")
        print("4 - Quality scoring")
        print("5 - Momentum scoring")
        print("6 - Risk scoring")
        print("7 - Composite scoring")
        print("0 - Exit")

        choice = input("Enter choice: ").strip()
        if choice == "0":
            break
        elif choice == "1":
            df = create_factors_file()
        elif choice == "2" and df is not None:
            df = dividend_scoring(df)
        elif choice == "3" and df is not None:
            df = value_scoring(df)
        elif choice == "4" and df is not None:
            df = quality_scoring(df)
        elif choice == "5" and df is not None:
            df = momentum_scoring(df)
        elif choice == "6" and df is not None:
            df = risk_scoring(df)
        elif choice == "7" and df is not None:
            df = composite_scoring(df)
        else:
            print("Invalid choice or CHDVD_with_factors.csv not available. Run option 1 first.")

# ---------------------------------------------
# Run menu
# ---------------------------------------------
if __name__ == "__main__":
    menu()

Admins: Feel free to move it to “coffe” if it’s too frivoulous :smiley:

1 Like