[
  {
    "path": ".gitignore",
    "content": "# Taken from https://github.com/github/gitignore/blob/master/Python.gitignore\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"python.pythonPath\": \"/usr/bin/python3\"\n}"
  },
  {
    "path": "README.md",
    "content": "# python-rsi\n\nRSI (Relative Strength Index) written in Python\n\n![Python RSI Image](https://github.com/mtamer/python-rsi/blob/master/src/images/example.png)\n\n## About\n\nRelative Strength Index written in Python. The whole point of this application is to be able to come up with a list of as many different types of stocks (stock tickers) that you want to screen and see if it meets the Relative Strength criteria. A combination of the RSI and the 20 and 200 day Moving Average (MA) tend to be strong and popular indicators to determine the future behavior of a stock.\n\nAt the end of pulling stock data, a table is shown displaying metrics of the stocks pulled along with a link to view the chart.\n\n![Stock Data Table](https://github.com/mtamer/python-rsi/blob/master/src/images/pulled-data-table.png)\n\n## To Install\n\n1. Clone repo: `git clone https://github.com/mtamer/python-rsi.git`\n2. `cd src`\n3. `pip install -r requirements.txt`\n4. Look inside the `main.py` file and put all the stocks you want to monitor in there or inside of `stocks.txt`\n5. To run: `python main.py`\n6. Enjoy!\n\n## Disclaimer\n\nThis repository is only for educational purposes. The owner and contributors of this repository are not responsible for how you use this code and the gains/losses you accumulate.\n"
  },
  {
    "path": "src/main.py",
    "content": "#!/usr/bin/env python3\n\nimport datetime\nimport random\nimport time\nfrom urllib.request import urlopen\n\nimport matplotlib\nimport matplotlib.dates as mdates\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as mticker\nimport numpy as np\nimport pandas_datareader.data as web\nimport pylab\nfrom mplfinance.original_flavor import candlestick_ohlc\nfrom pandas.core.common import flatten\nfrom tabulate import tabulate\n\nfrom stock import Stock\n\nmatplotlib.rcParams.update({'font.size': 9})\n\nstocks = []\n\n# If stocks array is empty, pull stock list from stocks.txt file\nstocks = stocks if len(stocks) > 0 else [\n    line.rstrip() for line in open(\"stocks.txt\", \"r\")]\n\n# Time frame you want to pull data from\nstart = datetime.datetime.now()-datetime.timedelta(days=365)\nend = datetime.datetime.now()\n\nif __name__ == \"__main__\":\n\n    # Array of moving averages you want to get\n    MAarr = [20, 200]\n\n    allData = []\n\n    for ticker in stocks:\n\n        try:\n            data = []\n\n            print(\"Pulling data for \" + ticker)\n\n            stock = Stock(ticker, start, end)\n\n            # Append data to array\n            data.append(ticker.upper())\n\n            data.append(stock.closes[-1])\n\n            for MA in MAarr:\n                computedSMA = stock.SMA(period=MA)\n                # print(computedSMA)\n                data.append(computedSMA[-1])\n\n            currentRsi = float(\"{:.2f}\".format(stock.rsi[-1]))\n\n            if currentRsi > 70:\n                data.append(str(currentRsi) + \" 🔥\")\n            elif currentRsi < 30:\n                data.append(str(currentRsi) + \" 🧊\")\n            else:\n                data.append(currentRsi)\n\n            chartLink = \"https://finance.yahoo.com/quote/\" + ticker + \"/chart?p=\" + ticker\n\n            data.append(chartLink)\n\n            allData.append(data)\n\n            # Shows chart only if current RSI is greater than or less than 70 or 30 respectively\n            if currentRsi < 30 or currentRsi > 70:\n\n                stock.graph(MAarr)\n\n        except Exception as e:\n            print('Error: ', str(e))\n\n    print(tabulate(allData, headers=flatten([\n        'Stock', 'Price', [str(x) + \" MA\" for x in MAarr], \"RSI\", \"chart\"])))\n"
  },
  {
    "path": "src/requirements.txt",
    "content": "astroid==2.4.2\nautopep8==1.5.3\ncertifi==2020.4.5.2\nchardet==3.0.4\ncolorama==0.4.3\ncycler==0.10.0\nidna==2.9\nisort==4.3.21\nkiwisolver==1.2.0\nlazy-object-proxy==1.4.3\nlxml==4.5.1\nmatplotlib==3.2.2\nmccabe==0.6.1\nmplfinance==0.12.5a3\nnumpy==1.18.5\npandas==1.0.5\npandas-datareader==0.8.1\npycodestyle==2.6.0\npylint==2.5.3\npyparsing==2.4.7\npython-dateutil==2.8.1\npytz==2020.1\nrequests==2.24.0\nsix==1.15.0\ntabulate==0.8.7 \ntoml==0.10.1\ntyped-ast==1.4.1\nurllib3==1.25.9\nwrapt==1.12.1\n"
  },
  {
    "path": "src/stock.py",
    "content": "#!/usr/bin/env python3\n\nimport datetime\nimport random\nfrom urllib.request import urlopen\n\nimport matplotlib\nimport matplotlib.dates as mdates\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as mticker\nimport numpy as np\nimport pandas_datareader.data as web\nimport pylab\nfrom mplfinance.original_flavor import candlestick_ohlc\n\nmatplotlib.rcParams.update({'font.size': 9})\n\n\nclass Stock:\n\n    ticker = None\n    dates = None\n    closes = None\n    highs = None\n    lows = None\n    opens = None\n    volumes = None\n    rsi = None\n\n    def __init__(self, ticker, start, end=datetime.datetime.now()):\n        self.ticker = ticker\n\n        \"\"\"\n        Different sources for pulling data can be found here:\n        https://readthedocs.org/projects/pandas-datareader/downloads/pdf/latest/\n        \"\"\"\n\n        stockData = web.DataReader(ticker, 'yahoo', start, end)\n\n        self.dates = [mdates.date2num(d) for d in stockData.index]\n        self.closes = stockData['Close']\n        self.highs = stockData['High']\n        self.lows = stockData['Low']\n        self.opens = stockData['Open']\n        self.volumes = stockData['Volume']\n\n        self.rsi = self.RSI(self.closes)\n\n    def RSI(self, prices, n=14):\n        deltas = np.diff(prices)\n        seed = deltas[:n+1]\n        up = seed[seed >= 0].sum()/n\n        down = -seed[seed < 0].sum()/n\n        rs = up/down\n        rsi = np.zeros_like(prices)\n        rsi[:n] = 100. - 100./(1.+rs)\n\n        for i in range(n, len(prices)):\n            delta = deltas[i-1]  # The diff is 1 shorter\n\n            if delta > 0:\n                upval = delta\n                downval = 0.\n            else:\n                upval = 0.\n                downval = -delta\n\n            up = (up*(n-1) + upval)/n\n            down = (down*(n-1) + downval)/n\n\n            rs = up/down\n            rsi[i] = 100. - 100./(1.+rs)\n\n        return rsi\n\n    def SMA(self, period, values=None):\n\n        values = self.closes if values is None else values\n\n        \"\"\"\n        Simple Moving Average. Periods are the time frame. For example, a period of 50 would be a 50 day\n        moving average. Values are usually the stock closes but can be passed any values\n        \"\"\"\n\n        weigths = np.repeat(1.0, period)/period\n        smas = np.convolve(values, weigths, 'valid')\n        return smas  # as a numpy array\n\n    def EMA(self, period, values=None):\n\n        values = self.closes if values is None else values\n\n        \"\"\"\n        Exponential Moving Average. Periods are the time frame. For example, a period of 50 would be a 50 day\n        moving average. Values are usually the stock closes but can be passed any values\n        \"\"\"\n\n        weights = np.exp(np.linspace(-1., 0., period))\n        weights /= weights.sum()\n        a = np.convolve(values, weights, mode='full')[:len(values)]\n        a[:period] = a[period]\n        return a\n\n    def MACD(self, x, slow=26, fast=12):\n        \"\"\"\n        Compute the MACD (Moving Average Convergence/Divergence) using a fast and slow exponential moving avg'\n        return value is emaslow, emafast, macd which are len(x) arrays\n        \"\"\"\n\n        emaslow = self.EMA(slow, x)\n        emafast = self.EMA(fast, x)\n        return emaslow, emafast, emafast - emaslow\n\n    def graph(self, movingAverageArr=[]):\n        try:\n\n            x = 0\n            y = len(self.dates)\n            newAr = []\n            while x < y:\n                appendLine = self.dates[x], self.opens[x], self.closes[x], self.highs[x], self.lows[x], self.volumes[x]\n                newAr.append(appendLine)\n                x += 1\n\n            # Fix this\n            SP = len(self.dates[200-1:])\n\n            fig = plt.figure(facecolor='#07000d')\n\n            ax1 = plt.subplot2grid(\n                (6, 4), (1, 0), rowspan=4, colspan=4, facecolor='#07000d')\n            candlestick_ohlc(ax1, newAr[-SP:], width=.6,\n                             colorup='#53c156', colordown='#ff1717')\n\n            for MA in movingAverageArr:\n\n                computedSMA = self.SMA(MA, self.closes)\n\n                # Used to generate random hex color to put on graph\n                def r(): return random.randint(0, 255)\n                randomColor = '#%02X%02X%02X' % (r(), r(), r())\n\n                maLabel = str(MA) + ' SMA'\n                ax1.plot(self.dates[-SP:], computedSMA[-SP:], randomColor,\n                         label=maLabel, linewidth=1.5)\n\n            ax1.grid(False, color='w')\n            ax1.xaxis.set_major_locator(mticker.MaxNLocator(10))\n            ax1.xaxis.set_major_formatter(\n                mdates.DateFormatter('%Y-%m-%d'))\n            ax1.yaxis.label.set_color(\"w\")\n            ax1.spines['bottom'].set_color(\"#5998ff\")\n            ax1.spines['top'].set_color(\"#5998ff\")\n            ax1.spines['left'].set_color(\"#5998ff\")\n            ax1.spines['right'].set_color(\"#5998ff\")\n            ax1.tick_params(axis='y', colors='w')\n            plt.gca().yaxis.set_major_locator(mticker.MaxNLocator(prune='upper'))\n            ax1.tick_params(axis='x', colors='w')\n            plt.ylabel('Stock price and Volume')\n\n            maLeg = plt.legend(loc=9, ncol=2, prop={'size': 7},\n                               fancybox=True, borderaxespad=0.)\n            maLeg.get_frame().set_alpha(0.4)\n            textEd = pylab.gca().get_legend().get_texts()\n            pylab.setp(textEd[0:5], color='w')\n\n            volumeMin = 0\n\n            ax0 = plt.subplot2grid(\n                (6, 4), (0, 0), sharex=ax1, rowspan=1, colspan=4, facecolor='#07000d')\n\n            rsiCol = '#c1f9f7'\n            posCol = '#386d13'\n            negCol = '#8f2020'\n\n            ax0.plot(self.dates[-SP:], self.rsi[-SP:],\n                     rsiCol, linewidth=1.5)\n            ax0.axhline(70, color=negCol)\n            ax0.axhline(30, color=posCol)\n            ax0.fill_between(self.dates[-SP:], self.rsi[-SP:], 70, where=(self.rsi[-SP:]\n                                                                          >= 70), facecolor=negCol, edgecolor=negCol, alpha=0.5)\n            ax0.fill_between(self.dates[-SP:], self.rsi[-SP:], 30, where=(self.rsi[-SP:]\n                                                                          <= 30), facecolor=posCol, edgecolor=posCol, alpha=0.5)\n            ax0.set_yticks([30, 70])\n            ax0.yaxis.label.set_color(\"w\")\n            ax0.spines['bottom'].set_color(\"#5998ff\")\n            ax0.spines['top'].set_color(\"#5998ff\")\n            ax0.spines['left'].set_color(\"#5998ff\")\n            ax0.spines['right'].set_color(\"#5998ff\")\n            ax0.tick_params(axis='y', colors='w')\n            ax0.tick_params(axis='x', colors='w')\n            plt.ylabel('RSI')\n\n            ax1v = ax1.twinx()\n            ax1v.fill_between(self.dates[-SP:], volumeMin,\n                              self.volumes[-SP:], facecolor='#00ffe8', alpha=.4)\n            ax1v.axes.yaxis.set_ticklabels([])\n            ax1v.grid(False)\n\n            # Edit this to 3, so it's a bit larger\n            ax1v.set_ylim(0, 3*self.volumes.max())\n            ax1v.spines['bottom'].set_color(\"#5998ff\")\n            ax1v.spines['top'].set_color(\"#5998ff\")\n            ax1v.spines['left'].set_color(\"#5998ff\")\n            ax1v.spines['right'].set_color(\"#5998ff\")\n            ax1v.tick_params(axis='x', colors='w')\n            ax1v.tick_params(axis='y', colors='w')\n            ax2 = plt.subplot2grid(\n                (6, 4), (5, 0), sharex=ax1, rowspan=1, colspan=4, facecolor='#07000d')\n            fillcolor = '#00ffe8'\n            nslow = 26\n            nfast = 12\n            nema = 9\n            emaslow, emafast, macd = self.MACD(self.closes)\n            ema9 = self.EMA(nema, macd)\n            ax2.plot(self.dates[-SP:], macd[-SP:],\n                     color='#4ee6fd', lw=2)\n            ax2.plot(self.dates[-SP:], ema9[-SP:],\n                     color='#e1edf9', lw=1)\n            ax2.fill_between(self.dates[-SP:], macd[-SP:]-ema9[-SP:], 0,\n                             alpha=0.5, facecolor=fillcolor, edgecolor=fillcolor)\n\n            plt.gca().yaxis.set_major_locator(mticker.MaxNLocator(prune='upper'))\n            ax2.spines['bottom'].set_color(\"#5998ff\")\n            ax2.spines['top'].set_color(\"#5998ff\")\n            ax2.spines['left'].set_color(\"#5998ff\")\n            ax2.spines['right'].set_color(\"#5998ff\")\n            ax2.tick_params(axis='x', colors='w')\n            ax2.tick_params(axis='y', colors='w')\n            plt.ylabel('MACD', color='w')\n            ax2.yaxis.set_major_locator(\n                mticker.MaxNLocator(nbins=5, prune='upper'))\n            for label in ax2.xaxis.get_ticklabels():\n                label.set_rotation(45)\n\n            plt.suptitle(self.ticker.upper(), color='w')\n\n            plt.setp(ax0.get_xticklabels(), visible=False)\n            plt.setp(ax1.get_xticklabels(), visible=False)\n\n            # ax1.annotate('Look Here!', (date[-1], Av1[-1]),\n            #              xytext=(0.8, 0.9), textcoords='axes fraction',\n            #              arrowprops=dict(facecolor='white', shrink=0.05),\n            #              fontsize=14, color='w',\n            #              horizontalalignment='right', verticalalignment='bottom')\n\n            plt.subplots_adjust(left=.09, bottom=.14,\n                                right=.94, top=.95, wspace=.20, hspace=0)\n\n            plt.show()\n            fig.savefig('example.png', facecolor=fig.get_facecolor())\n\n        except Exception as e:\n            print('Error graphing data: ', str(e))\n"
  },
  {
    "path": "src/stocks.txt",
    "content": "baba\nroku\nmsft\namzn\ntqqq\nnvda\nvgt\ntwtr\ndis\nt\nsnap\ntsla"
  }
]