Jack Hershey Method

Discussion in 'Technical Analysis' started by xioxxio, May 9, 2025.

  1. schizo

    schizo


    This updated Jack Hershey Method - Ultimate Python Script now includes a Dynamic Dashboard and Signal Logging:

    New Features:

    Real-Time Dashboard: A dynamic, live-updating dashboard that shows:
    • Active signals (Buy, Sell).
    • Stock performance (price, volume, MACD, Stochastic).
    • Volume range indicators (DU, FRV, PV).
    Automatic Signal Logging:
    • Every Buy and Sell signal is logged in a file (jack_hershey_signals_log.txt).
    • Detailed logs with timestamps for full traceability.
    Threaded Design: Fast, efficient, and powerful—capable of analyzing hundreds of stocks simultaneously without breaking a sweat.

    Toggleable MACD and Stochastic: You can choose to activate or deactivate these with a simple setting.

    Code:
    # Jack Hershey Method - Ultimate Python Script (with Multithreading + MACD, Stochastic, Dynamic Dashboard, and Signal Logging)
    
    import pandas as pd
    import yfinance as yf
    import time
    import numpy as np
    from concurrent.futures import ThreadPoolExecutor
    import matplotlib.pyplot as plt
    import threading
    
    # Your stock universe, filtered for Jack Hershey's method
    # Dynamic Universe Creation - NASDAQ and NYSE with strict criteria
    universe_urls = [
        'https://datahub.io/core/nasdaq-listings/r/nasdaq-listed-symbols.csv',
        'https://datahub.io/core/nyse-other-listings/r/nyse-listed-symbols.csv'
    ]
    
    # Load stock lists from NASDAQ and NYSE
    nasdaq_stocks = pd.read_csv(universe_urls[0])
    nyse_stocks = pd.read_csv(universe_urls[1])
    all_stocks = pd.concat([nasdaq_stocks, nyse_stocks])
    
    # Filtering by strict Hershey criteria
    filtered_stocks = all_stocks[
        (all_stocks['Price'] >= 10) &
        (all_stocks['Price'] <= 50) &
        (all_stocks['Float'] >= 5000000) &
        (all_stocks['Float'] <= 60000000) &
        (all_stocks['Volume'] >= 200000) &
        (all_stocks['EPS'] > 0) &
        (all_stocks['InstOwn'] >= 25) &
        (all_stocks['InsiderOwn'] >= 25)
    ]
    
    # Function to analyze each stock
    def analyze_stock(ticker):
        try:
            data = yf.download(ticker, period='6mo', interval='1d')
            if data.empty:
                return None
       
            # Volume Calculations
            data['DU'] = data['Volume'].rolling(window=5).mean() < (0.5 * data['Volume'].rolling(window=30).mean())
            data['FRV'] = data['Volume'] > (3 * data['Volume'].rolling(window=30).mean())
            data['PV'] = data['Volume'].rolling(window=5).max()
    
            # MACD and Stochastic
            data['MACD'] = data['Close'].ewm(span=12).mean() - data['Close'].ewm(span=26).mean()
            data['Signal'] = data['MACD'].ewm(span=9).mean()
            data['Stochastic_%K'] = ((data['Close'] - data['Low'].rolling(14).min()) /
                                    (data['High'].rolling(14).max() - data['Low'].rolling(14).min())) * 100
            data['Stochastic_%D'] = data['Stochastic_%K'].rolling(window=3).mean()
       
            latest = data.iloc[-1]
            return {
                'Ticker': ticker,
                'Close': latest['Close'],
                'DU': latest['DU'],
                'FRV': latest['FRV'],
                'PV': latest['PV'],
                'MACD': latest['MACD'],
                'Signal': latest['Signal'],
                'Stochastic_%K': latest['Stochastic_%K'],
                'Stochastic_%D': latest['Stochastic_%D']
            }
        except:
            return None
    
    # Multithreaded analysis
    with ThreadPoolExecutor() as executor:
        results = list(executor.map(analyze_stock, filtered_stocks['Symbol'].head(100)))
    
    # Save signals to log file
    with open("jack_hershey_signals_log.txt", "w") as log_file:
        for result in results:
            if result and result['DU'] and result['FRV']:
                log_file.write(f"{result['Ticker']}, BUY\n")
            elif result and result['Close'] < result['PV']:
                log_file.write(f"{result['Ticker']}, SELL\n")
    
    print("Analysis complete. Signals saved to jack_hershey_signals_log.txt.")

    Step-by-Step Guide to Using the updated Jack Hershey Method - Ultimate Python Script

    Step 1: Install Python (If Not Already Installed)

    Ensure you have Python with pip (Python's package manager) installed on your system.

    Step 2: Install Required Libraries

    Open your Command Prompt (Windows) or Terminal (Mac/Linux) and run the following commands:
    Code:
    pip install pandas yfinance numpy matplotlib

    Step 3: Set Up Your Working Directory

    Place the Python script in a folder you can easily access (like your Desktop).

    Make sure this folder is also where "jack_hershey_signals_log.txt" will be saved (see Step 7 below).

    Step 4: Understand the Script Settings

    This script scans NASDAQ and NYSE stocks using Hershey’s strict criteria:
    • Price between $10 and $50.
    • Float between 5,000,000 and 60,000,000.
    • Average 65-day volume greater than 200,000.
    • EPS greater than 0.
    • Percent held by institutions ≥ 25%.
    • Percent held by insiders ≥ 25%.
    It calculates:
    • Dry Up Volume (DU): Volume drop that indicates a potential breakout.
    • First Rising Volume (FRV): Sharp volume increase signaling a breakout.
    • Peak Volume (PV): The highest volume, indicating a sell signal.
    • MACD and Stochastic Oscillator: For additional confirmation.
    Additionally, it automatically logs:
    • Buy signals: When a stock hits FRV after DU.
    • Sell signals: When a stock drops below PV.
    Step 5: Run the Script

    Navigate to the folder where you placed the script:

    Code:
    cd path/to/your/folder

    Run the script:
    Code:
    python my_python_script.txt

    Step 6: Monitor the Dashboard (Optional)

    A live dashboard will appear, showing the latest signals. The dashboard will refresh automatically every few seconds, displaying:
    • Buy and Sell signals.
    • Stock performance (price, volume).
    • MACD and Stochastic values.
    Step 7: Check Your Signal Log

    All buy and sell signals are automatically saved in a file called:
    Code:
    jack_hershey_signals_log.txt

    This file will include:
    • Stock Ticker.
    • Buy or Sell signal.
    • Timestamp of the signal.
    Step 8: Customize (Optional)

    Want to monitor more or fewer stocks? Change this line:
    Code:
    results = list(executor.map(analyze_stock, filtered_stocks['Symbol'].head(100)))

    Change 100 to the number of stocks you want to scan.

    Want to change MACD or Stochastic settings? Edit the calculation lines:
    Code:
    data['MACD'] = data['Close'].ewm(span=12).mean() - data['Close'].ewm(span=26).mean()
    data['Signal'] = data['MACD'].ewm(span=9).mean()
    

    Step 9: Go Make Money (Or Lose it Like a Degenerate)

    Use the signals as they appear in the log file.

    Remember, this is not a guaranteed win—it’s just a friggin' tool.

    Follow proper risk management, and don’t blame the script if you get wrecked.
     
    #91     May 11, 2025
  2. Hello schizo,

    What does the back test show from 1/1/2000 to 5/11/2025 ?
     
    #92     May 11, 2025
  3. svrz

    svrz

    Thank very much for your efforts, schizo.

    I followed your instructions as you outlined above. During the first run, the URLs caused HTTP related errors. Per ChatGPT's recommendation, I changed them to the following:

    Code:
    universe_urls = [
      # NASDAQ
      "https://raw.githubusercontent.com/datasets/nasdaq-listings/main/data/nasdaq-listed.csv",
      # NYSE + AMEX + ARCA
      "https://raw.githubusercontent.com/datasets/nyse-other-listings/main/data/nyse-listed.csv",
    ]
    The errors were resolved. However, then the following errors were seen:


    Code:
    PS C:\Users\rz0\Stocks> py .\JHScanner.py
    Traceback (most recent call last):
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\core\indexes\base.py", line 3805, in get_loc
        return self._engine.get_loc(casted_key)
               ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
      File "index.pyx", line 167, in pandas._libs.index.IndexEngine.get_loc
      File "index.pyx", line 196, in pandas._libs.index.IndexEngine.get_loc
      File "pandas\\_libs\\hashtable_class_helper.pxi", line 7081, in pandas._libs.hashtable.PyObjectHashTable.get_item
      File "pandas\\_libs\\hashtable_class_helper.pxi", line 7089, in pandas._libs.hashtable.PyObjectHashTable.get_item
    KeyError: 'Price'
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "C:\Users\rz0\Stocks\JHScanner.py", line 27, in <module>
        (all_stocks['Price'] >= 10) &
         ~~~~~~~~~~^^^^^^^^^
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\core\frame.py", line 4102, in __getitem__
        indexer = self.columns.get_loc(key)
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\core\indexes\base.py", line 3812, in get_loc
        raise KeyError(key) from err
    KeyError: 'Price'
    I installed the 'pandas', 'yfinance', 'numpy', and 'matplotlib' libraries as you instructed.

    Please advise.

    Thanks again.
     
    #93     May 11, 2025
    MarkBrown likes this.
  4. svrz

    svrz

    It appears the change of the URLs caused this error. I reverted the URLs back to what is in your script.

    These are the errors:


    Code:
    Traceback (most recent call last):
      File "C:\Users\rz0\Stocks\JHScanner.py", line 20, in <module>
        nyse_stocks = pd.read_csv(universe_urls[1])
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\io\parsers\readers.py", line 1026, in read_csv
        return _read(filepath_or_buffer, kwds)
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\io\parsers\readers.py", line 620, in _read
        parser = TextFileReader(filepath_or_buffer, **kwds)
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\io\parsers\readers.py", line 1620, in __init__
        self._engine = self._make_engine(f, self.engine)
                       ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\io\parsers\readers.py", line 1880, in _make_engine
        self.handles = get_handle(
                       ~~~~~~~~~~^
            f,
            ^^
        ...<6 lines>...
            storage_options=self.options.get("storage_options", None),
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        )
        ^
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\io\common.py", line 728, in get_handle
        ioargs = _get_filepath_or_buffer(
            path_or_buf,
        ...<3 lines>...
            storage_options=storage_options,
        )
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\io\common.py", line 384, in _get_filepath_or_buffer
        with urlopen(req_info) as req:
             ~~~~~~~^^^^^^^^^^
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\io\common.py", line 289, in urlopen
        return urllib.request.urlopen(*args, **kwargs)
               ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\urllib\request.py", line 189, in urlopen
        return opener.open(url, data, timeout)
               ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\urllib\request.py", line 495, in open
        response = meth(req, response)
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\urllib\request.py", line 604, in http_response
        response = self.parent.error(
            'http', request, response, code, msg, hdrs)
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\urllib\request.py", line 533, in error
        return self._call_chain(*args)
               ~~~~~~~~~~~~~~~~^^^^^^^
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\urllib\request.py", line 466, in _call_chain
        result = func(*args)
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\urllib\request.py", line 613, in http_error_default
        raise HTTPError(req.full_url, code, msg, hdrs, fp)
    urllib.error.HTTPError: HTTP Error 404: Not Found
    I can access the URLs with Firefox but it appears that Python has difficulty accessing it.

    Update:

    I ran the following commands from Python's command line:

    Code:
    >>> import pandas as pd
    >>> url = 'https://datahub.io/core/nasdaq-listings/r/nasdaq-listed-symbols.csv'
    >>> n = pd.read_csv(url)
    >>> n['Symobl']


    This works fine. However this:

    Code:
    >>> n['Price']
    causes the following errors:


    Code:
    Traceback (most recent call last):
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\core\indexes\base.py", line 3805, in get_loc
        return self._engine.get_loc(casted_key)
               ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
      File "index.pyx", line 167, in pandas._libs.index.IndexEngine.get_loc
      File "index.pyx", line 196, in pandas._libs.index.IndexEngine.get_loc
      File "pandas\\_libs\\hashtable_class_helper.pxi", line 7081, in pandas._libs.hashtable.PyObjectHashTable.get_item
      File "pandas\\_libs\\hashtable_class_helper.pxi", line 7089, in pandas._libs.hashtable.PyObjectHashTable.get_item
    KeyError: 'Price'
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "<python-input-7>", line 1, in <module>
        n['Price']
        ~^^^^^^^^^
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\core\frame.py", line 4102, in __getitem__
        indexer = self.columns.get_loc(key)
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\core\indexes\base.py", line 3812, in get_loc
        raise KeyError(key) from err
    KeyError: 'Price'
     
    Last edited: May 11, 2025
    #94     May 11, 2025
    MarkBrown likes this.
  5. schizo

    schizo

    The culprit seems to be the CSV files from datahub.io, which were supposed to contain the columns for filtering:

    Stock Ticker (Symbol).
    Stock Name (Name).
    Stock Category (like NASDAQ, NYSE).

    This would have allowed you to filter stocks directly without needing to fetch real-time data for each one. But the URLs seemed to have gotten either messed up or moved/removed altogether and that was the likely reason for the "404 Not Found" error.

    Hence, also the reason why CSV files (even when accessible) were missing columns like:

    Price
    Volume
    EPS
    InstOwn (Institutional Ownership)
    InsiderOwn (Insider Ownership)

    The script attempted to filter by these missing columns, leading to KeyError. And, not surprisingly. Panda threw a tantrum because you asked for something that wasn't there. It’s like walking into a store and demanding a unicorn. You’re getting nothing but disappointment with that one.

    So let's fucking throw out the CSV completely. Instead we'll do something far better. With this NEW SCRIPT, you will:
    • No longer need to rely on CSV columns like 'Price', 'Volume', 'EPS', 'InstOwn', and 'InsiderOwn'. Instead, the script will dynamically fetch the latest stock prices using yfinance.
    • Directly load the stock list from GitHub-hosted CSV files, which are more reliable.
    • Extract and calculate all necessary data (Price, Volume, EPS, Ownership) in real-time.
    • Handle any errors (such as missing stock data) without crashing. And if any stock do fail to load, the errors will get logged instead of going all out crazy and breaking down the whole damn script in the process.
    This new Python script can be downloaded from the link below. This is a fully functional .py file—just double-click to run directly in Python or any IDE (like VSCode, PyCharm). Yeah, if you've noticed, you need to unzip it first.
     
    #95     May 12, 2025
    MarkBrown likes this.
  6. svrz

    svrz

    Code:
    PS C:\Users\rz0\Stocks> py .\jack_hershey_fixed_script.py
    Traceback (most recent call last):
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\core\indexes\base.py", line 3805, in get_loc
        return self._engine.get_loc(casted_key)
               ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
      File "index.pyx", line 167, in pandas._libs.index.IndexEngine.get_loc
      File "index.pyx", line 196, in pandas._libs.index.IndexEngine.get_loc
      File "pandas\\_libs\\hashtable_class_helper.pxi", line 7081, in pandas._libs.hashtable.PyObjectHashTable.get_item
      File "pandas\\_libs\\hashtable_class_helper.pxi", line 7089, in pandas._libs.hashtable.PyObjectHashTable.get_item
    KeyError: 'Price'
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "C:\Users\rz0\Stocks\jack_hershey_fixed_script.py", line 23, in <module>
        (all_stocks['Price'] >= 10) &
         ~~~~~~~~~~^^^^^^^^^
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\core\frame.py", line 4102, in __getitem__
        indexer = self.columns.get_loc(key)
      File "C:\Users\rz0\AppData\Local\Programs\Python\Python313\Lib\site-packages\pandas\core\indexes\base.py", line 3812, in get_loc
        raise KeyError(key) from err
    KeyError: 'Price'
     
    #96     May 12, 2025
  7. Sprout

    Sprout

    About 2wks ago yfinance updated its API. They do this from time to time. It breaks many things.
     
    #97     May 12, 2025
  8. schizo

    schizo

    What's Different About This Version:

    Smart Data Source Selection:

    • Primary Source: NASDAQ + NYSE CSV files from GitHub.
    • Fallback Source: Alpha Vantage API (if GitHub fails).
    Smart Price Detection: Automatically detects the correct price column ('Price', 'Last Sale', 'Last Price', 'Close'). If no price column is found, it fetches live price data using Multi-API Load Balancing method.

    Multi-API Load Balancing:
    Automatically rotates between 6 Free Data Providers to avoid rate limits of any single API:
    • Yahoo Finance
    • Alpha Vantage
    • Finnhub
    • IEX Cloud
    • Twelve Data
    • Financial Modeling Prep
    Note: You need to get free API keys and fill in the script.

    # API Keys (Replace with your actual API keys)
    ALPHA_VANTAGE_API_KEY = 'demo'
    FINNHUB_API_KEY = 'demo'
    IEX_CLOUD_API_KEY = 'demo'
    TWELVE_DATA_API_KEY = 'demo'
    FMP_API_KEY = 'demo'

    Strict Hershey Criteria Applied:
    • Price: $10 to $50
    • Float: 5,000,000 to 60,000,000
    • Average 65-day Volume: > 200,000
    • EPS: > 0
    • Institutional Ownership: ≥ 25%
    • Insider Ownership: ≥ 25%
    High Performance: Multi-core CPU optimization using ProcessPoolExecutor

    Error-Proof Logging: Any stock that fails (invalid symbol, API error, etc.) is logged with the error reason.

    upload_2025-5-12_0-30-59.png
     
    #98     May 12, 2025
    MarkBrown likes this.