diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b88c27e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig: https://EditorConfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +indent_style = space +indent_size = 2 +tab_width = 2 + +[*.py] +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9a5d6f3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize line endings. +* text=auto eol=lf diff --git a/.github/workflows/pylint.yml b/.github/workflows/lint-code.yml similarity index 69% rename from .github/workflows/pylint.yml rename to .github/workflows/lint-code.yml index 0d75c1f..8b9c6c9 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/lint-code.yml @@ -7,18 +7,23 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.8", "3.10", "3.12"] steps: - - uses: actions/checkout@v3 + + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + - name: Install dependencies run: | python -m pip install --upgrade pip - pip install stock-indicators==1.2.1 + pip install stock-indicators==1.3.1 pip install pylint + - name: Analysing the code with pylint run: | pylint $(git ls-files '*.py') diff --git a/.github/workflows/lint-pull-request.yml b/.github/workflows/lint-pull-request.yml index 0be571c..48eb9a3 100644 --- a/.github/workflows/lint-pull-request.yml +++ b/.github/workflows/lint-pull-request.yml @@ -1,4 +1,4 @@ -name: "Pull request" +name: Pull request on: pull_request_target: @@ -14,6 +14,7 @@ jobs: main: name: lint PR title runs-on: ubuntu-latest + steps: - uses: amannn/action-semantic-pull-request@v5 id: lint_pr_title @@ -29,9 +30,8 @@ jobs: # the default error message that is shown when the pattern doesn't match. # The variables `subject` and `title` can be used within the message. subjectPatternError: > - The subject "**{subject}**" found in the pull request title "*{title}*" - didn't match the configured pattern. Please ensure that the subject - starts with an uppercase character. + The subject "**{subject}**" in pull request "*{title}*" + needs to start with an uppercase character. # If the PR contains one of these newline-delimited labels, the # validation is skipped. If you want to rerun the validation when @@ -47,25 +47,22 @@ jobs: if: always() && (steps.lint_pr_title.outputs.error_message != null) with: header: pr-title-lint-error - message: > - ### Hey there and thank you for opening this pull request! 👋🏼 - - It looks like your proposed **_Pull request title_** needs to be adjusted. - - >🚩 **Error** » ${{ steps.lint_pr_title.outputs.error_message }} + message: | + ### ⚠️ Pull Request title needs adjustment - #### Pull request title naming convention + Your PR title doesn't match our naming convention: `type: Subject` - Our PR title name taxonomy is `type: Subject`, where **type** is typically - *feat*, *fix*, or *chore*, and **subject** is a phrase (proper noun) that starts - with a capitalized letter. The *chore* type usually has a subject that starts - with an action verb like *Add* or *Update*. Examples: + > [!CAUTION] + > ${{ steps.lint_pr_title.outputs.error_message }} - - _feat: Admin portal login_ - - _fix: Divide by zero bug in SMA_ - - _chore: Update packages_ - - _docs: Improve setup guidance_ + #### Valid examples + - `feat: Add new RSI indicator` + - `fix: Correct MACD calculation` + - `chore: Update documentation` + - `test: Add unit tests for EMA` + - `refactor: Optimize moving average logic` + See the [Conventional Commits specification](https://www.conventionalcommits.org) for more information. # Delete a previous comment when the issue has been resolved diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0c084f1..5829e65 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,12 @@ { "recommendations": [ "donjayamanne.python-extension-pack", + "ms-dotnettools.vscode-dotnet-runtime", + "ms-python.black-formatter", "ms-python.debugpy", + "ms-python.isort", + "ms-python.pylint", + "ms-python.python", "ms-python.vscode-pylance" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..607aa1c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll": "always", // default: "never" + "source.organizeImports": "always" // default: "never" + }, + "editor.formatOnSave": true, // default: false + + "isort.args": ["--profile", "black"], // default: [] + "isort.importStrategy": "fromEnvironment", // default: "useBundled" + + "pylint.importStrategy": "fromEnvironment", // default: "useBundled" + + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" // default: null + } +} diff --git a/README.md b/README.md index 8504bf7..9fba18d 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ -# Stock Indicators for Python: QuickStart guide + +# Stock Indicators for Python: QuickStart Guide -These are the detailed steps to setup a Python project and to run your first finanical market price analysis with the [Stock Indicators for Python](https://python.stockindicators.dev) PyPI library package. This guide is partly derived from the more detailed [Visual Studio Code Python Tutorial](https://code.visualstudio.com/docs/python/python-tutorial). +A beginner's guide to setting up and using the [Stock Indicators for Python](https://python.stockindicators.dev) library for financial market analysis. > [!TIP] -> **TLDR**, if you just want to quickly run this example, use these CLI commands: +> **TLDR**, for the impatient, run these commands to fast-forward this tutorial: > > ```bash > git clone https://github.com/facioquo/stock-indicators-python-quickstart.git > cd stock-indicators-python-quickstart > python -m venv .venv -> sh .venv/Scripts/activate +> source .venv/bin/activate # On Windows use: .venv\Scripts\activate > pip install stock-indicators > python main.py > ``` @@ -18,86 +19,63 @@ These are the detailed steps to setup a Python project and to run your first fin ## Install prerequisite software -These are the tools that I've already installed and will use in this guide. +Required software versions: -> [!NOTE] -> *Don't sweat the OS*. These instructions were written for Windows 11, but are the same for Mac and Linux OS; however, you may need different tool editions. +- [Python](https://www.python.org/downloads) ≥ 3.8 +- [.NET SDK](https://dotnet.microsoft.com/download) ≥ 6.0 +- [Git](https://git-scm.com/download) (any recent version) +- [VS Code](https://code.visualstudio.com/download) (recommended) -### Install Git - -- [Download and install Git for Windows](https://git-scm.com/download/win) for *git* and bash terminal CLI - -### Install Python v3 - -- [Download and install Python](https://www.python.org/downloads) - - > I installed `v3.12.4`, the latest LTS version, using *administrative privileges*, for all users, and chose to add Python to my environment PATH variables. We support `v3.8` or newer. - - ```bash - # test with bash terminal command - python --version - > Python 3.12.4 - ``` - -### Install the .NET SDK - -- [Download and install .NET SDK](https://dotnet.microsoft.com/en-us/download/visual-studio-sdks) - - > I installed `v8.0.303`, the latest LTS version. We support `v6` or newer. We *do not* support Mono. - - ```bash - # test with bash terminal command - dotnet --version - > 8.0.303 - ``` - -### Install the Visual Studio Code IDE - -- [Download and install VS Code](https://code.visualstudio.com/download) - -I also installed these recommended extensions: +VS Code Extensions: - [Python Extension Pack](https://marketplace.visualstudio.com/items?itemName=donjayamanne.python-extension-pack) (includes primary Python extension) - [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) - [Python Debugger](https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy) +```bash +# Verify installations +python --version # Should be ≥ 3.8 +dotnet --version # Should be ≥ 6.0 +``` + ## Setup your project -1. Create a new project folder. -2. Optional: initialize a *git repository* in this folder with `git init` bash command and add a Python flavored [`.gitignore`](.gitignore) file. I found this one in [the *gitignore* templates repo](https://github.com/github/gitignore/blob/4488915eec0b3a45b5c63ead28f286819c0917de/Python.gitignore). -3. Initialize Python workspace with a [virtual environment](https://docs.python.org/3/tutorial/venv.html#creating-virtual-environments) (a cached instance): +1. **Create a new project folder.** + +2. _**Optional**: initialize a `git` repository in this folder with `git init` bash command and add a Python flavored [`.gitignore`](.gitignore) file._ I found this one in [the _gitignore templates_ repo](https://github.com/github/gitignore/blob/4488915eec0b3a45b5c63ead28f286819c0917de/Python.gitignore). + +3. **Initialize Python workspace** with a [_virtual environment_](https://docs.python.org/3/tutorial/venv.html#creating-virtual-environments) (a cached instance): ```bash - # git bash commands # create environment python -m venv .venv # then activate it - sh .venv/Scripts/activate + source .venv/bin/activate # On Windows use: .venv\Scripts\activate ``` - > You can also use VSCode command: **Python: Create Environment ...** and then **Python: Select Interpreter** to pick your just created **venv** instance. When done correctly, you should have a `.venv` folder in the root of your project folder. There are other ways to initialize in a global environment; however, this is the recommended approach from [the Python tutorial](https://code.visualstudio.com/docs/python/python-tutorial) I'd mentioned above. + > You can also use VS Code command: **Python: Create Environment ...** and then **Python: Select Interpreter** to pick your just created **venv** instance. When done correctly, you should have a `.venv` folder in the root of your project folder. There are other ways to initialize in a global environment; however, this is the recommended approach from [the Python tutorial](https://code.visualstudio.com/docs/python/python-tutorial) I'd mentioned above. -4. Install the [`stock-indicators`](https://pypi.org/project/stock-indicators) package from PyPI +4. **Install the [`stock-indicators`](https://pypi.org/project/stock-indicators) package** from PyPI ```bash - # bash terminal command + # install the package pip install stock-indicators ``` - > I'm using `v1.3.0`, the latest version. You should see it installed in `.venv/Lib/site-packages`. + > I'm using `v1.3.1`, the latest version. You should see it installed in `.venv/Lib/site-packages`. ```bash - # test with bash terminal command + # verify the install pip freeze --local ``` ```console ... - clr-loader==0.2.6 + clr-loader==0.2.7 pycparser==2.22 - pythonnet==3.0.3 - stock-indicators==1.3.0 + pythonnet==3.0.5 + stock-indicators==1.3.1 typing_extensions==4.12.2 ... ``` @@ -106,9 +84,9 @@ I also installed these recommended extensions: It's time to start writing some code. -5. To start, add a [`quotes.csv`](quotes.csv) file containing historical financial market prices in OHLCV format. Use the one I put in this repo. You can worry about all the available [stock quote sources](https://github.com/DaveSkender/Stock.Indicators/discussions/579) later. +5. To start, **add a [`quotes.csv`](quotes.csv) file** containing historical financial market prices in OHLCV format. Use the one I put in this repo. You can worry about all the available [stock quote sources](https://github.com/DaveSkender/Stock.Indicators/discussions/579) later. -6. Create a [`main.py`](main.py) file and import the utilities we'll be using at the top of it. +6. **Create a [`main.py`](main.py) file and import the utilities we'll be using at the top of it.** ```python import csv @@ -117,82 +95,104 @@ It's time to start writing some code. from stock_indicators import indicators, Quote ``` -7. Import the data from [the CSV file](quotes.csv) and convert it into an iterable list of the `Quote` class. +7. **Import the data** from [the CSV file](quotes.csv) and convert it into an iterable list of the `Quote` class. ```python - # import each row of the csv file into a raw iterable string list - with open('quotes.csv', 'r', newline='', encoding="utf-8") as file: + # Get price history data from CSV file + with open("quotes.csv", "r", newline="", encoding="utf-8") as file: rows = list(csv.reader(file)) - file.close() - # parse each row into proper `Quote` format + # Convert rows into Quote objects that stock-indicators understands + # CSV returns strings, but Quote needs numbers for prices and volume quotes = [] - for row in rows[1:]: # skipping CSV file header row - quotes.append(Quote( - datetime.strptime(row[0], '%Y-%m-%d'), # date - row[1], # open - row[2], # high - row[3], # low - row[4], # close - row[5], # volume - )) + for row in rows[1:]: # skip header row + quotes.append( + Quote( + datetime.strptime(row[0], "%Y-%m-%d"), # date + row[1], # open + row[2], # high + row[3], # low + row[4], # close + row[5], # volume + ) + ) ``` - > These `quotes` can now be used by the `stock-indicators` library. For a quickstart that uses **pandas.DataFrame**, see our online *ReplIt* code example for the [Williams Fractal indicator](https://replit.com/@daveskender/Stock-Indicators-for-Python-Williams-Fractal). + > These `quotes` can now be used by the `stock-indicators` library. For a quickstart that uses **pandas.DataFrame**, see our online _ReplIt_ code example for the [Williams Fractal indicator](https://replit.com/@daveskender/Stock-Indicators-for-Python-Williams-Fractal). -8. Calculate [an indicator](https://python.stockindicators.dev/indicators/) from the `quotes` +8. **Calculate [an indicator](https://python.stockindicators.dev/indicators/)** from the `quotes` ```python - # calculate 5-period simple moving average + # Calculate 5-period Simple Moving Average results = indicators.get_sma(quotes, 5) ``` -9. Configure `results` for console output +9. **Configure `results`** for console output ```python - # show the first 30 periods, for brevity + # Show the results print("Date SMA") - for r in islice(results, 0, 30): - print(f"{r.date:%Y-%m-%d} {r.sma or ''}") + for r in islice(results, 0, 30): # show first 30 days + sma = f"{r.sma:.3f}" if r.sma else "" + print(f"{r.date:%Y-%m-%d} {sma}") ``` ## Run the code -10. Click the ***Run Python File in Terminal*** (►) play button in the top-right side of the VS Code editor to run the code, or execute from the commandline in your bash terminal. The SMA indicator output will print to the console. +10. Click the _**Run Python File in Terminal**_ (►) play button in the top-right side of the VS Code editor to run the code, or execute from the commandline in your bash terminal. The SMA indicator output will print to the console. + + ```bash + # from CLI (optional) + python main.py + ``` + + ```console + Date SMA + -------------------- + 2017-01-03 + 2017-01-04 + 2017-01-05 + 2017-01-06 + 2017-01-09 213.872 + 2017-01-10 214.102 + 2017-01-11 214.200 + 2017-01-12 214.226 + 2017-01-13 214.196 + 2017-01-17 214.156 + 2017-01-18 214.210 + 2017-01-19 213.986 + 2017-01-20 214.024 + ... + ``` + + > The small deviations shown in these raw results are normal for `double` floating point precision data types. They're not _programming errors_. Developers will usually truncate or round to fewer significant digits when displaying. We're showing 3 decimal places here. - ```bash - # from CLI (optional) - python main.py - ``` +**You've done it!** That's the end of this QuickStart guide. - ```console - Date SMA - 2017-01-03 - 2017-01-04 - 2017-01-05 - 2017-01-06 - 2017-01-09 213.87199999999999 - 2017-01-10 214.102 - 2017-01-11 214.2 - 2017-01-12 214.22599999999997 - 2017-01-13 214.196 - 2017-01-17 214.156 - 2017-01-18 214.20999999999998 - 2017-01-19 213.98600000000002 - 2017-01-20 214.02400000000003 - ... - ``` +--- - > The small deviations shown in these raw results are normal for `double` floating point precision data types. They're not *programming errors*. Developers will usually truncate or round to fewer significant digits when displaying. +## Common issues -**You've done it!** That's the end of this QuickStart guide. +- **Import errors**: Ensure you've activated the virtual environment +- **Runtime errors**: Verify .NET SDK installation +- **.NET loading issues**: On Linux/macOS, you may need additional dependencies + +## Next steps + +- Explore [all available indicators](https://python.stockindicators.dev/indicators/) +- Learn about [data sources](https://github.com/DaveSkender/Stock.Indicators/discussions/579) +- Join our [community discussions](https://github.com/DaveSkender/Stock.Indicators/discussions) + +## Getting help -## Still having trouble getting started? +Having trouble? Try these resources: -Ask a question in our [open community help and support discussions](https://github.com/DaveSkender/Stock.Indicators/discussions/categories/help-and-support). +- [Documentation](https://python.stockindicators.dev) +- [GitHub Discussions](https://github.com/DaveSkender/Stock.Indicators/discussions) +- [Stack Overflow](https://stackoverflow.com/questions/tagged/stock-indicators) -And if you end up building something wonderful, come back and [share it with us](https://github.com/DaveSkender/Stock.Indicators/discussions/categories/show-and-tell). We love 💖 to see all the creative ways people are using the library. +## Share your work -Good luck 🍀 and have fun in building your systems! +**Built something cool?** [Share it](https://github.com/DaveSkender/Stock.Indicators/discussions/categories/show-and-tell) with the community! --- @DaveSkender, July 2024 +— @DaveSkender, January 2025 diff --git a/main.py b/main.py index f434fda..5a8dce8 100644 --- a/main.py +++ b/main.py @@ -1,30 +1,39 @@ -"""Hello indicators!""" +"""Hello indicators!""" # calculate a Simple Moving Average (SMA) + +# Import what we need from stock-indicators package import csv from datetime import datetime from itertools import islice from stock_indicators import indicators, Quote -# import csv file into interable list -with open('quotes.csv', 'r', newline='', encoding="utf-8") as file: +# Step 1: Get price history data from CSV file +with open("quotes.csv", "r", newline="", encoding="utf-8") as file: rows = list(csv.reader(file)) - file.close() -# parse rows into quotes +# Convert rows into Quote objects that stock-indicators understands +# CSV returns strings, but Quote needs numbers for prices and volume quotes = [] -for row in rows[1:]: # skipping CSV file header row - quotes.append(Quote( - datetime.strptime(row[0], '%Y-%m-%d'), # date - row[1], # open - row[2], # high - row[3], # low - row[4], # close - row[5], # volume - )) +for row in rows[1:]: # skip header row + quotes.append( + Quote( + datetime.strptime(row[0], "%Y-%m-%d"), # date + row[1], # open + row[2], # high + row[3], # low + row[4], # close + row[5], # volume + ) + ) -# calculate 5-period simple moving average +# Step 2: Calculate 5-period Simple Moving Average results = indicators.get_sma(quotes, 5) -# show the first 30 results, for brevity +# Step 3: Show the results print("Date SMA") -for r in islice(results, 0, 30): - print(f"{r.date:%Y-%m-%d} {r.sma or ''}") +print("-" * 20) +for r in islice(results, 0, 30): # show first 30 days + sma = f"{r.sma:.3f}" if r.sma else "" + print(f"{r.date:%Y-%m-%d} {sma}") + +# Try other indicators at: +# https://python.stockindicators.dev/indicators