Skip to content

Financial technical and fundamental analysis indicator library for pystockdb.

License

Notifications You must be signed in to change notification settings

portfolioplus/pystockfilter

Repository files navigation

pystockfilter

Release Build CI Build PyPI - Downloads Coverage Status Codacy Badge

Create your own fundamental or chart based stock filter. All you need is a database set up with pystockdb.

built-in filters

technical filters

  • ADX
  • RSI
  • StockIsHot: Simple trend indicator
  • StockIsHotSecure: improved StockIsHot version.

fundamental filters

  • Lervermann
  • Piotroski F-Score
  • Price Target Score: analysts price targets compared with actual price

install

pip install pystockfilter

quick start

Build internal filters:

import logging
from pystockdb.db.schema.stocks import db

from pystockfilter.tool.build_internal_filters import BuildInternalFilters

# connect to database
arguments = {'db_args': {
    'provider': 'sqlite',
    'filename': db_path_test,
    'create_db': False
    }
}
db.bind(**arguments["db_args"])
db.generate_mapping()
# create internal filters for Adidas AG and Infineon
arguments = {'symbols': ['ADS.F', 'IFX.F']}
builder = BuildInternalFilters(arguments, logger)
builder.build()
# create internal filters for all stocks in database
arguments = {'symbols': ['ALL']}
builder = BuildInternalFilters(arguments, logger)
builder.build()

Build custom filters:

import logging
import math
from datetime import datetime

import numpy as np
import tulipy as ti
import yfinance as yf
from dateutil.relativedelta import relativedelta
from pony.orm import db_session, select
from pystockdb.db.schema.stocks import Price, Tag
from pystockfilter.filter.base_filter import BaseFilter
from pystockfilter.base.base_helper import BaseHelper
from pystockfilter.tool.build_filters import BuildFilters

# custom filter 
class DividendKings(BaseFilter):
    """
    Calculates median of last dividends
    """

    NAME = 'DividendKings'

    def __init__(self, arguments: dict, logger: logging.Logger):
        self.buy = arguments['args']['threshold_buy']
        self.sell = arguments['args']['threshold_sell']
        self.lookback = arguments['args']['lookback']
        self.max_yield = arguments['args']['max_div_yield']
        super(DividendKings, self).__init__(arguments, logger)

    @db_session
    def analyse(self):
        symbol = select(sym.name for sym in self.stock.price_item.symbols
                        if Tag.YAO in sym.item.tags.name).first()
        try:
            yao_item = yf.Ticker(symbol)
            data = yao_item.dividends
        except ValueError:
            raise RuntimeError("Couldn't load dividends for {}".format(symbol))

        dates = data.index.array
        drop = []
        # let us calculate the dividend yield
        for my_date in dates:
            price = Price.select(
                    lambda p: p.symbol.name == symbol
                    and p.date.date() == my_date.date()
                ).first()
            if price:
                div_yield = (data[my_date] / price.close) * 100
                if div_yield > self.max_yield:
                    drop.append(my_date)
                    self.logger.error(
                        '{} has a non plausible div yield at {} ({} = {} / {} * 100).'
                        .format(symbol, my_date, div_yield, data[my_date], price.close)
                    )
                else:
                    data[my_date] = div_yield
            else:
                drop.append(my_date)
        data = data.drop(labels=drop)
        self.calc = data.median(axis=0)
        if self.calc is None or math.isnan(self.calc):
            raise RuntimeError("Couldn't calculate dividend yield.")
        return super(DividendKings, self).analyse()

    def get_calculation(self):
        return self.calc

    def look_back_date(self):
        return self.now_date + relativedelta(months=-self.lookback)

logger = BaseHelper.setup_logger("custom filter")

arguments_div = {
    "name": "DividendKings",
    "bars": False,
    "index_bars": False,
    "args": {
        "threshold_buy": 3,
        "threshold_sell": 0.2,
        "intervals": None,
        "max_div_yield": 9,
        "lookback": 2,
    },
}

symbols = ["ADS.F", "WDI.F", "BAYN.F"]

config_custom_filter = {
    "symbols": symbols,
    "filters": [DividendKings(arguments_div, logger)],
}

custom = BuildFilters(config_custom_filter, logger)
custom.build()

issue tracker

https://github.com/portfolioplus/pystockfilter/issuese