diff --git a/.coderabbit.yaml b/.coderabbit.yaml
index 60711dc9..8f166db2 100644
--- a/.coderabbit.yaml
+++ b/.coderabbit.yaml
@@ -131,8 +131,11 @@ reviews:
- "!*-lock.json" # NPM/PNPM lock files
- "!packages.lock.json" # NuGet lock file
- "!packages/" # NuGet packages cache
+ - "!_cslib/" # DLL library path
- "!.jekyll-cache/" # Jekyll cache
- "!.pytest_cache/" # Python test cache
+ - "!__pycache__/" # Python caches
+ - "!.venv/" # Python virtual env
- "!.benchmarks/" # Benchmarks cache
- "!.coverage/" # Code coverage reports
- "!vendor/" # Ruby vendor and gem files
@@ -165,7 +168,7 @@ reviews:
enabled: true
osvScanner: # Vulnerability package scanner
enabled: true
- pylint: # Python static analyzer
+ ruff: # Python linter and formatter
enabled: true
semgrep: # Security & code quality static analysis
enabled: true
@@ -225,9 +228,9 @@ reviews:
enabled: false
prismaLint: # Prisma schema linter
enabled: false
- regal: # Rego linter and language server
+ pylint: # Python static analyzer
enabled: false
- ruff: # Python linter and formatter
+ regal: # Rego linter and language server
enabled: false
rubocop: # Ruby linter and code formatter
enabled: false
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 85748491..32609b39 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -9,36 +9,20 @@
*/
{
"name": "Stock Indicators for Python",
- "image": "mcr.microsoft.com/devcontainers/python:3.12",
- "forwardPorts": [],
- "remoteUser": "vscode",
+ "image": "mcr.microsoft.com/devcontainers/python:3.13",
"features": {
- "ghcr.io/devcontainers/features/git:1": {
- "version": "os-provided"
- },
"ghcr.io/devcontainers/features/dotnet:2": {
- "version": "lts"
+ "version": "10.0",
+ "additionalVersions": "9.0,8.0"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "lts",
"pnpmVersion": "none",
"nvmVersion": "none"
},
- "ghcr.io/devcontainers/features/github-cli:1": {
- "installDirectlyFromGitHubRelease": true,
- "version": "latest"
- },
- "ghcr.io/devcontainers/features/azure-cli:1": {
- "version": "latest"
- },
+ "ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/ruby:1": {
"version": "3.3"
- },
- "ghcr.io/devcontainers-extra/features/isort:2": {
- "version": "latest"
- },
- "ghcr.io/devcontainers-extra/features/pylint:2": {
- "version": "latest"
}
},
// Use 'settings' to set *default* container specific settings.json
@@ -48,26 +32,39 @@
// container overrides only
// otherwise use .vscode/settings.json
"settings": {
- "pylint.importStrategy": "fromEnvironment",
- "python.defaultInterpreterPath": "/usr/local/bin/python"
- },
- // required extensions
- // for recommended, see .vscode/extensions.json
- "extensions": [
- "donjayamanne.python-extension-pack",
- "DavidAnson.vscode-markdownlint",
- "EditorConfig.EditorConfig",
- "ms-python.black-formatter",
- "ms-python.debugpy",
- "ms-python.isort",
- "ms-python.python",
- "ms-python.pylint",
- "ms-python.vscode-pylance"
- ]
- }
- },
- // Runs after the container is created
- "postCreateCommand": "chmod +x .devcontainer/setup.sh && .devcontainer/setup.sh",
- // Runs every time the container starts
- "postStartCommand": "echo 'Container started'"
-}
+ "python.defaultInterpreterPath": "${containerWorkspaceFolder}/.venv/bin/python",
+ "python.testing.pytestEnabled": true,
+ "python.testing.unittestEnabled": false,
+ "[python]": {
+ "editor.defaultFormatter": "charliermarsh.ruff",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.organizeImports.ruff": "explicit",
+ "source.fixAll.ruff": "explicit"
+ }
+ },
+ // required extensions
+ // for recommended, see .vscode/extensions.json
+ "extensions": [
+ "charliermarsh.ruff",
+ "DavidAnson.vscode-markdownlint",
+ "EditorConfig.EditorConfig",
+ "ms-python.debugpy",
+ "ms-python.python",
+ "ms-python.vscode-pylance"
+ ]
+ }
+ },
+ "forwardPorts": [
+ 4000
+ ],
+ "portsAttributes": {
+ "4000": {
+ "label": "Doc Site (Jekyll)",
+ "onAutoForward": "notify"
+ }
+ },
+ "remoteUser": "vscode",
+ "postCreateCommand": ".devcontainer/post-create.sh",
+ "postStartCommand": "echo 'Container started'"
+ }
diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh
new file mode 100644
index 00000000..03560b04
--- /dev/null
+++ b/.devcontainer/post-create.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+set -e
+
+# Create virtual environment if it doesn't exist
+if [ ! -d ".venv" ]; then
+ echo "Creating virtual environment..."
+ python -m venv .venv
+fi
+
+# Activate virtual environment
+source .venv/bin/activate
+
+# Upgrade pip
+echo "Upgrading pip..."
+python -m pip install --upgrade pip
+
+# Install core dependencies
+echo "Installing core dependencies..."
+pip install -r requirements.txt
+
+# Install test dependencies
+echo "Installing test dependencies..."
+pip install -r requirements-test.txt
+
+echo "✓ Dev container setup complete!"
diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh
deleted file mode 100755
index 7ad8e56e..00000000
--- a/.devcontainer/setup.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-# install or upgrade pip
-python -m ensurepip --upgrade
-
-# install core dependencies
-pip install -r requirements.txt
-
-# install test dependencies
-pip install -r requirements-test.txt
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..dcd91906
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,63 @@
+name: CI
+
+on:
+ push:
+ pull_request:
+
+permissions:
+ contents: read
+
+jobs:
+ linting:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout source
+ uses: actions/checkout@v6
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.x
+ dotnet-quality: ga
+
+ - name: Setup Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: 3.13
+ cache: "pip"
+
+ - name: Create virtual environment
+ run: python -m venv .venv
+
+ - name: Install dependencies
+ run: |
+ source .venv/bin/activate
+ python -m pip install --upgrade pip
+ python -m pip install -e .
+ python -m pip install -r requirements-test.txt
+
+ - name: Ruff lint
+ run: |
+ source .venv/bin/activate
+ ruff check .
+
+ - name: Ruff format check
+ run: |
+ source .venv/bin/activate
+ ruff format --check .
+
+ - name: Pyright
+ run: |
+ source .venv/bin/activate
+ pyright
+
+ - name: Pytest
+ run: |
+ source .venv/bin/activate
+ pytest
+
+ - name: pip-audit
+ run: |
+ source .venv/bin/activate
+ pip-audit -r requirements.txt -r requirements-test.txt
diff --git a/.github/workflows/deploy-package.yml b/.github/workflows/deploy-package.yml
index 2e35e2d8..22df88cb 100644
--- a/.github/workflows/deploy-package.yml
+++ b/.github/workflows/deploy-package.yml
@@ -19,15 +19,15 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
with:
fetch-tags: true
fetch-depth: 0
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
- python-version: 3.12
+ python-version: 3.13
- name: Build library
run: |
diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml
index b58c2f68..96de73a9 100644
--- a/.github/workflows/deploy-website.yml
+++ b/.github/workflows/deploy-website.yml
@@ -29,7 +29,7 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup Ruby
uses: ruby/setup-ruby@v1
diff --git a/.github/workflows/lint-pull-request.yml b/.github/workflows/lint-pull-request.yml
index 56118d78..95c01a2b 100644
--- a/.github/workflows/lint-pull-request.yml
+++ b/.github/workflows/lint-pull-request.yml
@@ -1,26 +1,48 @@
-name: Pull request
+name: Lint pull request
on:
pull_request_target:
+ branches:
+ - "main"
+ - "v[0-9]*"
types:
- opened
- edited
- unlabeled
+ - ready_for_review
+
+concurrency:
+ group: >-
+ ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
permissions:
pull-requests: write
jobs:
- main:
- name: lint PR title
+ title:
runs-on: ubuntu-latest
+ if: ${{ !github.event.pull_request.draft }}
steps:
- - uses: amannn/action-semantic-pull-request@v5
+ - uses: amannn/action-semantic-pull-request@v6.1.1
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
+ types: |
+ feat
+ fix
+ docs
+ style
+ refactor
+ perf
+ test
+ build
+ ci
+ chore
+ revert
+ plan
subjectPattern: ^([A-Z]).+$
subjectPatternError: >
The subject "**{subject}**" must start with an uppercase character.
@@ -28,8 +50,9 @@ jobs:
ignoreLabels: |
bot
dependencies
+ automated
- - uses: marocchino/sticky-pull-request-comment@v2
+ - uses: marocchino/sticky-pull-request-comment@v2.9.4
if: always() && (steps.lint_pr_title.outputs.error_message != null)
with:
header: pr-title-lint-error
@@ -47,6 +70,7 @@ jobs:
- `feat: Add API endpoint for market data`
- `fix: Resolve WebSocket connection issues`
+ - `plan: Define technical implementation approach`
- `chore: Update NuGet dependencies`
@@ -56,29 +80,33 @@ jobs:
- `feat: Add API endpoint for market data`
- `fix: Resolve WebSocket connection issues`
+ #### Planning & architecture
+ - `plan: Define technical implementation approach`
+
#### Code quality
- `style: Format trading strategy classes`
- `refactor: Restructure trading engine components`
- `perf: Optimize trade order execution flow`
-
+
#### Documentation & testing
- `docs: Update API documentation`
- `test: Add unit tests for sign-in flow`
-
+
#### Infrastructure
- - `build: Update .NET SDK version to 8.0`
+ - `build: Update .NET SDK version to 10.0`
- `ci: Add workflow for performance testing`
- `chore: Update NuGet dependencies`
-
+
#### Other
- `revert: Remove faulty market data provider`
-
- See [Conventional Commits](https://www.conventionalcommits.org) for more details.
+
+ See [Conventional Commits](https://www.conventionalcommits.org)
+ for more details.
# Delete a previous comment when the issue has been resolved
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
- uses: marocchino/sticky-pull-request-comment@v2
+ uses: marocchino/sticky-pull-request-comment@v2.9.4
with:
header: pr-title-lint-error
delete: true
diff --git a/.github/workflows/test-code-coverage.yml b/.github/workflows/test-code-coverage.yml
index adb0063b..bc860047 100644
--- a/.github/workflows/test-code-coverage.yml
+++ b/.github/workflows/test-code-coverage.yml
@@ -6,7 +6,7 @@ name: Test code coverage
on:
push:
branches: ["main"]
- pull_request_target:
+ pull_request:
branches: ["*"]
workflow_dispatch:
@@ -27,20 +27,20 @@ jobs:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
steps:
- - name: Checkout PR
- uses: actions/checkout@v5
+ - name: Checkout source
+ uses: actions/checkout@v6
with:
# Explicitly checkout PR code
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup .NET
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@v5
with:
- dotnet-version: 9.x
+ dotnet-version: 10.x
dotnet-quality: ga
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
python-version: 3.13
cache: "pip"
diff --git a/.github/workflows/test-indicators-full.yml b/.github/workflows/test-indicators-full.yml
index de8685d3..b802f433 100644
--- a/.github/workflows/test-indicators-full.yml
+++ b/.github/workflows/test-indicators-full.yml
@@ -23,26 +23,26 @@ jobs:
# Primary testing on Ubuntu (free tier)
- os: ubuntu-24.04-arm
python-version: "3.8"
- dotnet-version: "6.x"
- - os: ubuntu-24.04-arm
- python-version: "3.10"
dotnet-version: "8.x"
- os: ubuntu-24.04-arm
- python-version: "3.12"
+ python-version: "3.10"
dotnet-version: "9.x"
-
+ - os: ubuntu-24.04-arm
+ python-version: "3.13"
+ dotnet-version: "10.x"
+
# Essential platform compatibility testing (reduced matrix)
- os: windows-2025
python-version: "3.12"
- dotnet-version: "9.x"
+ dotnet-version: "10.x"
- os: macos-15
- python-version: "3.12"
- dotnet-version: "9.x"
-
+ python-version: "3.13"
+ dotnet-version: "10.x"
+
# Legacy support verification
- os: ubuntu-22.04
python-version: "3.8"
- dotnet-version: "6.x"
+ dotnet-version: "8.x"
runs-on: ${{ matrix.os }}
name: "Py${{ matrix.python-version }}/.NET${{ matrix.dotnet-version }} on ${{ matrix.os }}"
@@ -54,16 +54,16 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup .NET
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{ matrix.dotnet-version }}
dotnet-quality: ga
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
diff --git a/.github/workflows/test-indicators.yml b/.github/workflows/test-indicators.yml
index ca1341ff..06ead104 100644
--- a/.github/workflows/test-indicators.yml
+++ b/.github/workflows/test-indicators.yml
@@ -20,14 +20,14 @@ jobs:
include:
- # Primary testing on Ubuntu (free tier) with older configuration
+ # Primary testing on Ubuntu (free tier) with minimum supported .NET version
- os: ubuntu-22.04
- dotnet-version: "6.x"
+ dotnet-version: "8.x"
python-version: "3.8"
# Primary testing on Ubuntu (free tier) with newer configuration
- os: ubuntu-24.04-arm
- dotnet-version: "9.x"
+ dotnet-version: "10.x"
python-version: "3.13"
post-summary: true
@@ -46,10 +46,10 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Check debug settings
- if: ${{ matrix.post-summary }} == 'true'
+ if: ${{ matrix['post-summary'] == true }}
shell: bash
run: |
echo "Checking for debug logging settings in package files..."
@@ -64,13 +64,13 @@ jobs:
echo "✓ No debug logging settings found."
- name: Setup .NET
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{ matrix.dotnet-version }}
dotnet-quality: ga
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
@@ -97,6 +97,6 @@ jobs:
- name: Post test summary
uses: test-summary/action@v2
- if: ${{ matrix.post-summary }} == 'true'
+ if: ${{ matrix['post-summary'] == true }}
with:
paths: test-results.xml
diff --git a/.github/workflows/test-localization.yml b/.github/workflows/test-localization.yml
index 832fad9a..29e2daa3 100644
--- a/.github/workflows/test-localization.yml
+++ b/.github/workflows/test-localization.yml
@@ -48,16 +48,17 @@ jobs:
PYTHONIOENCODING: utf-8
steps:
- - uses: actions/checkout@v5
+ - name: Checkout source
+ uses: actions/checkout@v6
- - uses: actions/setup-dotnet@v4
+ - uses: actions/setup-dotnet@v5
with:
- dotnet-version: 9.x
+ dotnet-version: 10.x
dotnet-quality: ga
- - uses: actions/setup-python@v5
+ - uses: actions/setup-python@v6
with:
- python-version: 3.12
+ python-version: 3.13
cache: "pip"
- name: Configure Linux locale
diff --git a/.github/workflows/test-performance.yml b/.github/workflows/test-performance.yml
index 76e651c4..176bad1d 100644
--- a/.github/workflows/test-performance.yml
+++ b/.github/workflows/test-performance.yml
@@ -21,20 +21,20 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@v4
+ uses: actions/setup-dotnet@v5
with:
- dotnet-version: 9.x
+ dotnet-version: 10.x
dotnet-quality: ga
- name: Setup Python
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
- python-version: 3.12
+ python-version: 3.13
cache: "pip"
- name: Setup GitVersion
diff --git a/.github/workflows/test-website-a11y.yml b/.github/workflows/test-website-a11y.yml
index 0f925299..ed7f6542 100644
--- a/.github/workflows/test-website-a11y.yml
+++ b/.github/workflows/test-website-a11y.yml
@@ -29,7 +29,7 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup Ruby
uses: ruby/setup-ruby@v1
diff --git a/.github/workflows/test-website-links.yml b/.github/workflows/test-website-links.yml
index da87bd15..affe3233 100644
--- a/.github/workflows/test-website-links.yml
+++ b/.github/workflows/test-website-links.yml
@@ -29,7 +29,7 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout@v5
+ uses: actions/checkout@v6
- name: Setup Ruby
uses: ruby/setup-ruby@v1
diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc
new file mode 100644
index 00000000..d11a7dc6
--- /dev/null
+++ b/.markdownlint-cli2.jsonc
@@ -0,0 +1,57 @@
+{
+ "globs": [
+ "**/*.md",
+ "**/*.markdown"
+ ],
+ "ignores": [
+ "**/.pytest_cache/**",
+ "**/.coverage/**",
+ "**/.git/**",
+ "**/.github/**",
+ "**/.venv/**",
+ "**/_site/**",
+ "**/__pycache__/**",
+ "**/_cslib/**",
+ "**/node_modules/**",
+ "**/TestResults/**",
+ "**/*playwright*/**",
+ "**/vendor/**"
+ ],
+ "config": {
+ "default": true,
+ "MD003": {
+ "style": "atx"
+ },
+ "MD004": {
+ "style": "dash"
+ },
+ "MD007": {
+ "indent": 2
+ },
+ "MD013": false,
+ "MD025": false,
+ "MD029": {
+ "style": "ordered"
+ },
+ "MD033": {
+ "allowed_elements": [
+ "a",
+ "code",
+ "details",
+ "summary",
+ "sub",
+ "sup",
+ "kbd",
+ "abbr",
+ "img",
+ "br"
+ ]
+ },
+ "MD046": {
+ "style": "fenced"
+ },
+ "MD048": {
+ "style": "backtick"
+ }
+ }
+}
diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index 3340ad6c..00000000
--- a/.pylintrc
+++ /dev/null
@@ -1,21 +0,0 @@
-[MASTER]
-extension-pkg-allow-list=
- clr
-
-ignore-long-lines=yes
-
-ignore-imports=yes
-
-disable=
- C0103, # Variable name doesn't conform to snake_case naming style
- C0114, # Missing module docstring
- C0115, # Missing class docstring
- C0116, # Missing function or method docstring
- C0301, # Line too long
- C0321, # More than one statement on a single line
- C0413, # Import should be at the top of the file
- C0415, # Import outside toplevel
- E0401, # Import error
- W0212, # Access to a protected member of a client class
- R0903, # Too few public methods
- R0913 # Too many arguments
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 61fe2e05..091bc9a5 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,13 +1,10 @@
{
"recommendations": [
- "donjayamanne.python-extension-pack",
+ "charliermarsh.ruff",
"DavidAnson.vscode-markdownlint",
"EditorConfig.EditorConfig",
- "ms-python.black-formatter",
"ms-python.debugpy",
- "ms-python.isort",
"ms-python.python",
- "ms-python.pylint",
"ms-python.vscode-pylance"
]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 5df0aa08..42007812 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -10,12 +10,6 @@
"web_search"
],
"github.copilot.chat.tools.memory.enabled": true,
- "isort.args": [
- "--profile",
- "black"
- ],
- "isort.check": true,
- "isort.importStrategy": "fromEnvironment", // default: "useBundled"
"markdownlint.config": {
"default": true, // Enable all default rules
"MD013": false, // Disable line length checking entirely
@@ -29,18 +23,22 @@
},
"MD041": false // Allow content before first heading
},
- "pylint.importStrategy": "fromEnvironment", // default: "useBundled"
"python.testing.pytestArgs": [
"tests"
],
+ "python.defaultInterpreterPath": ".venv/bin/python",
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
- "[markdown]": {
- "editor.defaultFormatter": "DavidAnson.vscode-markdownlint",
- "files.trimTrailingWhitespace": true
- },
"[python]": {
- "editor.defaultFormatter": "ms-python.black-formatter"
- },
- "python.analysis.typeCheckingMode": "basic"
+ "editor.defaultFormatter": "charliermarsh.ruff",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.organizeImports.ruff": "explicit",
+ "source.fixAll.ruff": "explicit"
+ },
+ "[markdown]": {
+ "editor.defaultFormatter": "DavidAnson.vscode-markdownlint",
+ "files.trimTrailingWhitespace": true
+ }
+ }
}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 6b21e7fa..29f562d4 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -35,12 +35,12 @@
{
"label": "Install: Python Packages",
"type": "shell",
- "command": "pip install -r requirements.txt && pip install -r requirements-test.txt"
+ "command": "python -m pip install -r requirements.txt && python -m pip install -r requirements-test.txt"
},
{
"label": "Update: Python Packages",
"type": "shell",
- "command": "pip install -U -r requirements.txt && pip install -U -r requirements-test.txt"
+ "command": "python -m pip install -U -r requirements.txt && python -m pip install -U -r requirements-test.txt"
},
{
"label": "Update: Ruby Packages (docs)",
@@ -65,8 +65,37 @@
{
"label": "Build",
"type": "shell",
- "command": "pip install -r requirements.txt && pip install -r requirements-test.txt",
- "group": "build"
+ "command": "python -m pip install -r requirements.txt && python -m pip install -r requirements-test.txt",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ {
+ "label": "Lint: Code",
+ "type": "shell",
+ "command": "python -m ruff check . && python -m ruff format --check .",
+ "group": "test"
+ },
+ {
+ "label": "Lint: Code (fix)",
+ "type": "shell",
+ "command": "python -m ruff check --fix . && python -m ruff format ."
+ },
+ {
+ "label": "Lint: Markdown files",
+ "detail": "Run markdownlint over documentation",
+ "type": "shell",
+ "command": "echo y | npx markdownlint-cli2",
+ "group": "test",
+ "problemMatcher": "$markdownlint"
+ },
+ {
+ "label": "Lint: Markdown files (fix)",
+ "detail": "Auto-fix markdown formatting issues",
+ "type": "shell",
+ "command": "echo y | npx markdownlint-cli2 --fix",
+ "problemMatcher": "$markdownlint"
},
{
"label": "Test: All",
@@ -82,28 +111,31 @@
{
"label": "Test: Unit (default)",
"type": "shell",
- "command": "pytest -vr A",
- "group": "test"
+ "command": "python -m pytest -vr A",
+ "group": {
+ "kind": "test",
+ "isDefault": true
+ }
},
{
"label": "Test: Coverage",
"type": "shell",
- "command": "pytest --cov=stock_indicators --cov-report=term-missing"
+ "command": "python -m pytest --cov=stock_indicators --cov-report=term-missing"
},
{
"label": "Test: Performance",
"type": "shell",
- "command": "pytest -m performance"
+ "command": "python -m pytest -m performance"
},
{
"label": "Test: Performance (focus)",
"type": "shell",
- "command": "pytest -m \"performance and perf_focus\""
+ "command": "python -m pytest -m \"performance and perf_focus\""
},
{
"label": "Test: Localization",
"type": "shell",
- "command": "pytest -m localization -vr A"
+ "command": "python -m pytest -m localization -vr A"
},
{
"label": "Run: Doc Site with LiveReload",
@@ -123,50 +155,32 @@
{
"label": "Benchmark: JSON (prompt filename)",
"type": "shell",
- "command": "pytest -m performance --benchmark-json=.benchmarks/${input:benchJsonOut}",
- "options": {
- "cwd": "${workspaceFolder}"
- }
+ "command": "python -m pytest -m performance --benchmark-json=.benchmarks/${input:benchJsonOut}"
},
{
"label": "Benchmark: Focus JSON (prompt filename)",
"type": "shell",
- "command": "pytest -m \"performance and perf_focus\" --benchmark-json=.benchmarks/${input:benchJsonOut}",
- "options": {
- "cwd": "${workspaceFolder}"
- }
+ "command": "python -m pytest -m \"performance and perf_focus\" --benchmark-json=.benchmarks/${input:benchJsonOut}"
},
{
"label": "Benchmark: Autosave to .benchmarks",
"type": "shell",
- "command": "pytest -m performance --benchmark-autosave --benchmark-save-data",
- "options": {
- "cwd": "${workspaceFolder}"
- }
+ "command": "python -m pytest -m performance --benchmark-autosave --benchmark-save-data"
},
{
"label": "Benchmark: Focus Autosave to .benchmarks",
"type": "shell",
- "command": "pytest -m \"performance and perf_focus\" --benchmark-autosave --benchmark-save-data",
- "options": {
- "cwd": "${workspaceFolder}"
- }
+ "command": "python -m pytest -m \"performance and perf_focus\" --benchmark-autosave --benchmark-save-data"
},
{
"label": "Benchmark: List Saved Runs",
"type": "shell",
- "command": "pytest-benchmark list",
- "options": {
- "cwd": "${workspaceFolder}"
- }
+ "command": "pytest-benchmark list"
},
{
"label": "Benchmark: Compare Saved Runs",
"type": "shell",
- "command": "pytest-benchmark compare ${input:benchBase} ${input:benchTarget}",
- "options": {
- "cwd": "${workspaceFolder}"
- }
+ "command": "pytest-benchmark compare ${input:benchBase} ${input:benchTarget}"
}
],
"inputs": [
diff --git a/README.md b/README.md
index 1b32e6cc..e2d33019 100644
--- a/README.md
+++ b/README.md
@@ -22,9 +22,9 @@ Visit our project site for more information:
### Windows
-1. Install .NET SDK (6.0 or newer):
+1. Install .NET SDK (8.0 or newer):
- Download from [Microsoft .NET Downloads](https://dotnet.microsoft.com/download)
- - Or using winget: `winget install Microsoft.DotNet.SDK.6`
+ - Or using winget: `winget install Microsoft.DotNet.SDK.8`
- Verify: `dotnet --info`
2. Install the package:
@@ -35,7 +35,7 @@ Visit our project site for more information:
### macOS
-1. Install .NET SDK (6.0 or newer):
+1. Install .NET SDK (8.0 or newer):
```bash
brew install dotnet-sdk
diff --git a/benchmarks/conftest.py b/benchmarks/conftest.py
index 28aaf09e..c6518ee3 100644
--- a/benchmarks/conftest.py
+++ b/benchmarks/conftest.py
@@ -35,7 +35,7 @@ def get_data_from_csv(filename):
data_path = quotes_dir / f"{filename}.csv"
logger.debug("Loading benchmark data from: %s", data_path)
- with open(data_path, "r", newline="", encoding="utf-8") as csvfile:
+ with open(data_path, newline="", encoding="utf-8") as csvfile:
reader = csv.reader(csvfile)
data = list(reader)
return data[1:] # skips the first row, those are headers
@@ -62,9 +62,10 @@ def parse_date(date_str):
@pytest.fixture(scope="session")
-def raw_data(filename: str = 'Default'):
+def raw_data(filename: str = "Default"):
return get_data_from_csv(filename)
+
@pytest.fixture(scope="session")
def quotes(days: int = 502):
rows = get_data_from_csv("Default")
diff --git a/benchmarks/test_benchmark_indicators.py b/benchmarks/test_benchmark_indicators.py
index 5a18555e..ef3f2ca5 100644
--- a/benchmarks/test_benchmark_indicators.py
+++ b/benchmarks/test_benchmark_indicators.py
@@ -6,7 +6,6 @@
@pytest.mark.performance
class TestPerformance:
-
def test_benchmark_adl(self, benchmark, quotes):
benchmark(indicators.get_adl, quotes)
@@ -262,17 +261,20 @@ def test_benchmark_converting_to_IndicatorResults(self, benchmark, quotes):
from stock_indicators._cslib import CsIndicator
from stock_indicators.indicators.common.enums import CandlePart
from stock_indicators.indicators.common.quote import Quote
- from stock_indicators.indicators.sma import SMAResults, SMAResult
+ from stock_indicators.indicators.sma import SMAResult, SMAResults
candle_part: CandlePart = CandlePart.CLOSE
lookback_periods = 12
- quotes = Quote.use(quotes * 1000, candle_part) # Error occurs if not assigned to local var.
+ quotes = Quote.use(
+ quotes * 1000, candle_part
+ ) # Error occurs if not assigned to local var.
results = CsIndicator.GetSma(quotes, lookback_periods)
benchmark(SMAResults, results, SMAResult)
def test_benchmark_converting_to_CsDecimal(self, benchmark, raw_data):
from stock_indicators._cstypes import Decimal as CsDecimal
+
raw_data = raw_data * 1000
def convert_to_quotes(rows):
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
index b7b42e09..30c558ea 100644
--- a/docs/Gemfile.lock
+++ b/docs/Gemfile.lock
@@ -40,7 +40,7 @@ GEM
ffi (>= 1.15.0)
eventmachine (1.2.7)
execjs (2.10.0)
- faraday (2.13.4)
+ faraday (2.14.0)
faraday-net_http (>= 2.0, < 3.5)
json
logger
@@ -286,7 +286,7 @@ GEM
unicode-display_width (1.8.0)
uri (1.0.3)
wdm (0.2.0)
- webrick (1.9.1)
+ webrick (1.9.2)
PLATFORMS
x64-mingw-ucrt
diff --git a/docs/_indicators/AtrStop.md b/docs/_indicators/AtrStop.md
index bcd6979e..a31a03ba 100644
--- a/docs/_indicators/AtrStop.md
+++ b/docs/_indicators/AtrStop.md
@@ -89,7 +89,6 @@ Created by Welles Wilder, the ATR Trailing Stop indicator attempts to determine

-
### Sources
- [C# core]({{site.dotnet.src}}/a-d/AtrStop/AtrStop.Series.cs)
diff --git a/docs/contributing.md b/docs/contributing.md
index 5f9b786c..7e1b7a00 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -50,11 +50,9 @@ For new features, submit an issue with the `enhancement` label.
## Development Environment (Quick Setup)
-- Recommended tools: Git, Node.js, npm, Docker, Python, Docker Desktop, Visual Studio Code (see `.vscode/extensions.json` for recommended extensions).
-- This project supports [VS Code Dev Containers](https://code.visualstudio.com/docs/remote/containers) for a consistent development environment. Open the project in VS Code and select "Reopen in Container" (requires the Remote - Containers extension).
-- You can test GitHub Actions workflows locally using [`act`](https://github.com/nektos/act), which is preinstalled in the devcontainer. Example: `act -l` to list workflows, `act` to run all workflows.
-
-For more details, see the [official documentation](https://github.com/nektos/act#readme) and the project README.
+- Recommended tools: Git, Python 3.8+, Docker (optional), and Visual Studio Code (see `.vscode/extensions.json` for recommended extensions).
+- This project supports [VS Code Dev Containers](https://code.visualstudio.com/docs/remote/containers) for a consistent development environment. Open the project in VS Code and select "Reopen in Container" (requires the Dev Containers extension).
+- Local installs are plain `pip + venv`; no Poetry/Conda/Hatch required.
---
@@ -68,10 +66,10 @@ For more details, see the [official documentation](https://github.com/nektos/act
### Windows Setup
-1. Install .NET SDK (6.0 or newer):
+1. Install .NET SDK (8.0 or newer):
```powershell
- winget install Microsoft.DotNet.SDK.6
+ winget install Microsoft.DotNet.SDK.8
# Or download from https://dotnet.microsoft.com/download
```
@@ -80,13 +78,15 @@ For more details, see the [official documentation](https://github.com/nektos/act
```powershell
git clone https://github.com/facioquo/stock-indicators-python.git
cd stock-indicators-python
- pip install -r requirements.txt
- pip install -r requirements-test.txt
+ python -m venv .venv
+ .venv\Scripts\python -m pip install --upgrade pip
+ .venv\Scripts\python -m pip install -e .
+ .venv\Scripts\python -m pip install -r requirements-test.txt
```
### macOS Setup
-1. Install .NET SDK (6.0 or newer):
+1. Install .NET SDK (8.0 or newer):
```bash
brew install dotnet-sdk
@@ -97,13 +97,16 @@ For more details, see the [official documentation](https://github.com/nektos/act
```bash
git clone https://github.com/facioquo/stock-indicators-python.git
cd stock-indicators-python
- pip install -r requirements.txt
- pip install -r requirements-test.txt
+ python -m venv .venv
+ source .venv/bin/activate
+ python -m pip install --upgrade pip
+ python -m pip install -e .
+ python -m pip install -r requirements-test.txt
```
## Testing
-- We use [pytest](https://docs.pytest.org) for testing.
+- We use [Ruff](https://docs.astral.sh/ruff/) for linting/formatting, [Pyright](https://microsoft.github.io/pyright/) for type checking, and [pytest](https://docs.pytest.org) for tests. `pip-audit` runs in CI.
- Review the `tests` folder for examples of unit tests. Just copy one of these.
- New indicators should be tested against manually calculated, proven, accurate results. It is helpful to include your manual calculations spreadsheet in the appropriate indicator folder when [submitting changes](#submitting-changes).
- Historical Stock Quotes are automatically added as pytest fixtures. The various `.csv` files in the `samples` folder are used in the unit tests. See `tests/conftest.py` for their usage. A `History.xlsx` Excel file is also included in the `samples` folder that contains the same information but separated by sheets. Use this for your manual calculations to ensure that it is correct. Do not commit changes to this Excel file.
@@ -112,41 +115,38 @@ For more details, see the [official documentation](https://github.com/nektos/act
### Running Tests
+Common commands (after activating `.venv`):
+
```bash
-# install core dependencies
-pip install -r requirements.txt
+# lint and format
+python -m ruff check .
+python -m ruff format --check .
-# install dependencies
-pip install -r requirements-test.txt
+# type-check
+python -m pyright
# run standard unit tests
-pytest
+python -m pytest
```
-To run different types of tests, use the following commands:
-
-- **Normal unit tests** (default):
-
- ```bash
- pytest
- ```
+To run different types of tests:
- **Non-standard `localization` tests**:
```bash
- pytest -m "localization"
+ python -m pytest -m "localization"
```
- **Performance tests**:
```bash
- pytest -m "performance"
+ python -m pytest -m "performance"
```
- **All tests** (not recommended):
```bash
- pytest -m ""
+ python -m pytest -m ""
```
You can also use the `-svr A` arguments with pytest to get more detailed output:
@@ -161,14 +161,14 @@ pytest -svr A
### Performance benchmarking
-Running the commands below in your console will show performance data. You can find the latest results [here]({{site.baseurl}}/performance/).
+Running the commands below in your console will produce [benchmark performance data](https://python.stockindicators.dev/performance/) that we include on our documentation site.
```bash
# install dependencies
-pip install -r requirements-test.txt
+python -m pip install -r requirements-test.txt
# run performance tests
-pytest -m "performance"
+python -m pytest -m "performance"
```
## Documentation
diff --git a/docs/pages/guide.md b/docs/pages/guide.md
index 1cc644c1..53c9fe5f 100644
--- a/docs/pages/guide.md
+++ b/docs/pages/guide.md
@@ -31,11 +31,11 @@ layout: page
> Install **Python** and the **.NET SDK**. Use the latest versions for better performance.
| Installer | Min | Latest | Download |
- |---| :---: | :---: | --- |
- | Python | 3.8 | 3.12 | [@python.org](https://www.python.org/downloads/) |
- | .NET SDK | 6.0 | 8.0 | [@microsoft.com](https://dotnet.microsoft.com/en-us/download) |
+ | --- | :---: | :---: | --- |
+ | Python | 3.8 | 3.13 | [@python.org](https://www.python.org/downloads/) |
+ | .NET SDK | 8.0 | 10.0 | [@microsoft.com](https://dotnet.microsoft.com/en-us/download) |
- Note: we do not support the open source [Mono .NET Framework](https://www.mono-project.com).
+ Note: we do not support the open source [Mono .NET Framework](https://www.mono-project.com). Python 3.14+ is not yet supported due to `pythonnet` compatibility.
2. Install the **stock-indicators** Python package into your environment.
@@ -127,7 +127,7 @@ from stock_indicators.indicators.common.quote import Quote
[[source]](https://github.com/facioquo/stock-indicators-python/blob/main/stock_indicators/indicators/common/quote.py)
| name | type | notes |
-| -- |-- |-- |
+| ---- | ---- | ----- |
| date | [`datetime.datetime`](https://docs.python.org/3.8/library/datetime.html#datetime.datetime) | Date |
| open | [`decimal.Decimal`](https://docs.python.org/3.8/library/decimal.html?highlight=decimal#decimal.Decimal), Optional | Open price |
| high | [`decimal.Decimal`](https://docs.python.org/3.8/library/decimal.html?highlight=decimal#decimal.Decimal), Optional | High price |
@@ -289,7 +289,7 @@ from stock_indicators.indicators.common.enums import Match
```
| type | description |
-|-- |:-- |
+| ---- | :---------- |
| `Match.BULL_CONFIRMED` | Confirmation of a prior bull Match |
| `Match.BULL_SIGNAL` | Matching bullish pattern |
| `Match.BULL_BASIS` | Bars supporting a bullish Match |
@@ -303,23 +303,23 @@ from stock_indicators.indicators.common.enums import Match
The `CandleProperties` class is an extended version of `Quote`, and contains additional calculated properties.
-| name | type | notes |
-| -- |-- |-- |
-| `date` | datetime | Date |
-| `open` | Decimal | Open price |
-| `high` | Decimal | High price |
-| `low` | Decimal | Low price |
-| `close` | Decimal | Close price |
-| `volume` | Decimal | Volume |
-| `size` | Decimal, Optional | `high-low` |
-| `body` | Decimal, Optional | `|open-close|` |
-| `upper_wick` | Decimal, Optional | Upper wick size |
-| `lower_wick` | Decimal, Optional | Lower wick size |
-| `body_pct` | float, Optional | `body/size` |
-| `upper_wick_pct` | float, Optional | `upper_wick/size` |
-| `lower_wick_pct` | float, Optional | `lower_wick/size` |
-| `is_bullish` | bool | `close>open` direction |
-| `is_bearish` | bool | `closeopen` direction |
+| `is_bearish` | bool | `close=3.0.0",
+ "pythonnet>=3.0.5",
"typing_extensions>=4.4.0"
]
keywords = [
@@ -61,3 +61,56 @@ exclude = ["tests*", "benchmarks*", "test_data*", "docs*"]
# can be empty if no extra settings are needed, presence enables setuptools_scm
[tool.setuptools_scm]
local_scheme = "no-local-version"
+
+[tool.ruff]
+target-version = "py38"
+line-length = 88
+
+[tool.ruff.lint]
+select = [
+ "E", # pycodestyle errors
+ "F", # pyflakes
+ "I", # import sorting
+ "B", # flake8-bugbear
+ "UP", # pyupgrade
+ "C4", # flake8-comprehensions
+ "TID",# flake8-tidy-imports
+ "PT", # flake8-pytest-style
+ "RUF" # Ruff-specific rules
+]
+ignore = [
+ "B905", # allow zip without strict length check on py38 support floor
+ "E501" # allow longer docstrings/comments
+]
+
+[tool.ruff.lint.isort]
+known-first-party = ["stock_indicators"]
+
+[tool.ruff.lint.per-file-ignores]
+"stock_indicators/__init__.py" = ["F401", "F403"]
+"stock_indicators/_cslib/__init__.py" = ["F401"]
+"stock_indicators/_cstypes/__init__.py" = ["F401"]
+"stock_indicators/indicators/__init__.py" = ["F401"]
+
+[tool.ruff.format]
+quote-style = "double"
+
+[tool.pyright]
+include = ["stock_indicators", "tests"]
+pythonVersion = "3.8"
+typeCheckingMode = "standard"
+useLibraryCodeForTypes = true
+reportMissingImports = "none"
+reportMissingModuleSource = "none"
+reportMissingTypeStubs = "none"
+stubPath = "typings"
+exclude = ["typings"]
+reportInvalidTypeForm = "none"
+reportAttributeAccessIssue = "none"
+reportOperatorIssue = "none"
+reportReturnType = "none"
+reportGeneralTypeIssues = "none"
+reportArgumentType = "none"
+reportInconsistentOverload = "none"
+reportIndexIssue = "none"
+reportCallIssue = "none"
diff --git a/requirements-test.txt b/requirements-test.txt
index 48573f14..90f51398 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -3,3 +3,6 @@ pytest
pytest-cov
pytest-benchmark
backports.zoneinfo; python_version < '3.9'
+ruff
+pyright
+pip-audit
diff --git a/requirements.txt b/requirements.txt
index 9045a0c0..91659dac 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,3 @@
# _cslib
-pythonnet>=3.0.0
+pythonnet>=3.0.5
typing_extensions>=4.4.0
diff --git a/stock_indicators/__init__.py b/stock_indicators/__init__.py
index f2d5deb1..c7561952 100644
--- a/stock_indicators/__init__.py
+++ b/stock_indicators/__init__.py
@@ -15,4 +15,11 @@
"""
from stock_indicators import indicators
+from stock_indicators.exceptions import (
+ IndicatorCalculationError,
+ StockIndicatorsError,
+ StockIndicatorsInitializationError,
+ TypeConversionError,
+ ValidationError,
+)
from stock_indicators.indicators.common import *
diff --git a/stock_indicators/_cslib/__init__.py b/stock_indicators/_cslib/__init__.py
index defaa45b..90d3b8b0 100644
--- a/stock_indicators/_cslib/__init__.py
+++ b/stock_indicators/_cslib/__init__.py
@@ -1,96 +1,158 @@
"""
Skender.Stock.Indicators
~~~~~~~~~~~~~~~~~~~~~~~~
+# pylint: disable=duplicate-code # Property patterns are expected to repeat
-This module loads `Skender.Stock.Indicators.dll`(v2.6.1), which is a compiled library
+This module loads `Skender.Stock.Indicators.dll`(v2.7.1), which is a compiled library
package from , written in C#.
-The target framework of dll is `.NET 6.0`.
+The target framework of dll is `.NET 8.0`.
"""
import logging
import platform
from pathlib import Path
+from typing import Any
from pythonnet import load
-# Setup logging
+from stock_indicators.exceptions import StockIndicatorsInitializationError
from stock_indicators.logging_config import configure_logging
+# Setup logging
configure_logging(debug=False) # Set to True if you need debug this module
logger = logging.getLogger(__name__)
-try:
- # Load CLR
- load(runtime="coreclr")
- import clr
- logger.debug("CLR loaded successfully on %s", platform.system())
- # Get absolute paths
- base_path = Path(__file__).parent.resolve()
- dll_path = base_path / "lib" / "Skender.Stock.Indicators.dll"
-
- # Set assembly resolve path
- from System import IO, AppDomain
+def _initialize_clr() -> Any:
+ """Initialize the CLR runtime."""
+ try:
+ load(runtime="coreclr")
+ import clr as clr_module
+
+ logger.debug("CLR loaded successfully on %s", platform.system())
+ return clr_module
+ except Exception as e:
+ init_error_msg = (
+ "Failed to load .NET CLR runtime.\n"
+ "Please ensure .NET 8.0+ is installed: https://dotnet.microsoft.com/download\n"
+ f"Platform: {platform.system()}\n"
+ f"Error: {e!s}"
+ )
+ raise StockIndicatorsInitializationError(init_error_msg) from e
+
+
+def _setup_assembly_probing(assembly_dll_path: Path) -> None:
+ """Setup assembly probing path for .NET dependency resolution."""
+ try:
+ from System import IO, AppDomain
+
+ current_domain = AppDomain.CurrentDomain
+ assembly_path = IO.Path.GetDirectoryName(str(assembly_dll_path))
+ current_domain.SetData("PROBING_PATH", assembly_path)
+ logger.debug("Set assembly probing path to: %s", assembly_path)
+ except Exception as e: # pylint: disable=broad-exception-caught
+ # Broad exception catch is necessary for C# interop
+ logger.warning("Failed to set assembly probing path: %s", e)
- current_domain = AppDomain.CurrentDomain
- assembly_path = IO.Path.GetDirectoryName(str(dll_path))
- current_domain.SetData("PROBING_PATH", assembly_path)
- logger.debug("Set assembly probing path to: %s", assembly_path)
+def _load_assembly(assembly_dll_path: Path):
+ """Load the Stock Indicators assembly."""
try:
- # Load the assembly first
from System.Reflection import Assembly
- logger.debug("Loading assembly from: %s", dll_path)
- assembly = Assembly.LoadFile(str(dll_path))
- logger.debug("Assembly loaded: %s", assembly.FullName)
+ if not assembly_dll_path.exists():
+ raise FileNotFoundError(f"Assembly not found at: {assembly_dll_path}")
+
+ logger.debug("Loading assembly from: %s", assembly_dll_path)
+ loaded_assembly = Assembly.LoadFile(str(assembly_dll_path))
+ logger.debug("Assembly loaded: %s", loaded_assembly.FullName)
- # Add reference after successful load
- clr.AddReference(assembly.FullName) # pylint: disable=no-member
- logger.debug("Assembly reference added")
+ return loaded_assembly
+ except Exception as e:
+ load_error_msg = (
+ f"Failed to load Stock Indicators assembly from: {assembly_dll_path}\n"
+ "Please ensure the .NET assembly is present and compatible.\n"
+ f"Error: {e!s}"
+ )
+ raise StockIndicatorsInitializationError(load_error_msg) from e
- except Exception as asm_error:
- logger.error("Error loading assembly: %s", str(asm_error))
- if hasattr(asm_error, "LoaderExceptions"):
- for ex in asm_error.LoaderExceptions:
- logger.error("Loader exception: %s", str(ex))
- raise
+
+def _add_assembly_reference(loaded_assembly, clr_module) -> None:
+ """Add reference to the loaded assembly."""
+ try:
+ clr_module.AddReference(loaded_assembly.FullName) # pylint: disable=no-member
+ logger.debug("Assembly reference added successfully")
+ except Exception as e:
+ ref_error_msg = (
+ f"Failed to add reference to assembly: {loaded_assembly.FullName}\n"
+ f"Error: {e!s}"
+ )
+ raise StockIndicatorsInitializationError(ref_error_msg) from e
+
+
+try:
+ # Initialize CLR
+ clr = _initialize_clr()
+
+ # Get assembly path
+ base_path = Path(__file__).parent.resolve()
+ dll_path = base_path / "lib" / "Skender.Stock.Indicators.dll"
+
+ # Setup assembly probing
+ _setup_assembly_probing(dll_path)
+
+ # Load assembly
+ assembly = _load_assembly(dll_path)
+
+ # Add assembly reference
+ _add_assembly_reference(assembly, clr)
except Exception as e:
- logger.error("Detailed error information: %s", str(e))
- if hasattr(e, "__cause__") and e.__cause__ is not None:
- logger.error("Caused by: %s", str(e.__cause__))
+ # Re-raise our custom exception or wrap unexpected errors
+ if not isinstance(e, StockIndicatorsInitializationError):
+ error_msg = (
+ "Stock Indicators initialization failed due to unexpected error.\n"
+ "Please ensure .NET 8.0+ is installed: https://dotnet.microsoft.com/download\n"
+ f"Error: {e!s}"
+ )
+ raise StockIndicatorsInitializationError(error_msg) from e
+ raise
+
+# Library modules (common) - Import after successful initialization
+try:
+ from Skender.Stock.Indicators import BetaType as CsBetaType
+ from Skender.Stock.Indicators import CandlePart as CsCandlePart
+ from Skender.Stock.Indicators import CandleProperties as CsCandleProperties
+ from Skender.Stock.Indicators import ChandelierType as CsChandelierType
+ from Skender.Stock.Indicators import EndType as CsEndType
+ from Skender.Stock.Indicators import Indicator as CsIndicator
+ from Skender.Stock.Indicators import Match as CsMatch
+ from Skender.Stock.Indicators import MaType as CsMaType
+ from Skender.Stock.Indicators import PeriodSize as CsPeriodSize
+ from Skender.Stock.Indicators import PivotPointType as CsPivotPointType
+ from Skender.Stock.Indicators import PivotTrend as CsPivotTrend
+ from Skender.Stock.Indicators import Quote as CsQuote
+ from Skender.Stock.Indicators import QuoteUtility as CsQuoteUtility
+ from Skender.Stock.Indicators import ResultBase as CsResultBase
+ from Skender.Stock.Indicators import ResultUtility as CsResultUtility
+
+ # Built-in System types
+ from System import DateTime as CsDateTime
+ from System import Decimal as CsDecimal
+ from System import Enum as CsEnum
+ from System.Collections.Generic import IEnumerable as CsIEnumerable
+ from System.Collections.Generic import List as CsList
+ from System.Globalization import CultureInfo as CsCultureInfo
+ from System.Globalization import NumberStyles as CsNumberStyles
+
+ logger.info("Stock Indicators library initialized successfully")
+
+except ImportError as e:
error_msg = (
- "Stock Indicators initialization failed.\n"
- "Please ensure .NET 6.0+ is installed: https://dotnet.microsoft.com/download\n"
- f"Error: {str(e)}"
+ "Failed to import Stock Indicators types after successful assembly loading.\n"
+ "This may indicate a version mismatch or missing dependencies.\n"
+ f"Error: {e!s}"
)
- raise ImportError(error_msg) from e
-
-# Library modules (common)
-from Skender.Stock.Indicators import BetaType as CsBetaType
-from Skender.Stock.Indicators import CandlePart as CsCandlePart
-from Skender.Stock.Indicators import CandleProperties as CsCandleProperties
-from Skender.Stock.Indicators import ChandelierType as CsChandelierType
-from Skender.Stock.Indicators import EndType as CsEndType
-from Skender.Stock.Indicators import Indicator as CsIndicator
-from Skender.Stock.Indicators import Match as CsMatch
-from Skender.Stock.Indicators import MaType as CsMaType
-from Skender.Stock.Indicators import PeriodSize as CsPeriodSize
-from Skender.Stock.Indicators import PivotPointType as CsPivotPointType
-from Skender.Stock.Indicators import PivotTrend as CsPivotTrend
-from Skender.Stock.Indicators import Quote as CsQuote
-from Skender.Stock.Indicators import QuoteUtility as CsQuoteUtility
-from Skender.Stock.Indicators import ResultBase as CsResultBase
-from Skender.Stock.Indicators import ResultUtility as CsResultUtility
-
-# Built-in
-from System import DateTime as CsDateTime
-from System import Decimal as CsDecimal
-from System import Enum as CsEnum
-from System.Collections.Generic import IEnumerable as CsIEnumerable
-from System.Collections.Generic import List as CsList
-from System.Globalization import CultureInfo as CsCultureInfo
-from System.Globalization import NumberStyles as CsNumberStyles
+ raise StockIndicatorsInitializationError(error_msg) from e
diff --git a/stock_indicators/_cslib/lib/Skender.Stock.Indicators.dll b/stock_indicators/_cslib/lib/Skender.Stock.Indicators.dll
index 5351569d..454e6c60 100644
Binary files a/stock_indicators/_cslib/lib/Skender.Stock.Indicators.dll and b/stock_indicators/_cslib/lib/Skender.Stock.Indicators.dll differ
diff --git a/stock_indicators/_cslib/lib/Skender.Stock.Indicators.xml b/stock_indicators/_cslib/lib/Skender.Stock.Indicators.xml
new file mode 100644
index 00000000..23d4382f
--- /dev/null
+++ b/stock_indicators/_cslib/lib/Skender.Stock.Indicators.xml
@@ -0,0 +1,2742 @@
+
+
+
+ Skender.Stock.Indicators
+
+
+
+
+ Accumulation/Distribution Line (ADL) is a rolling accumulation of Chaikin Money Flow Volume.
+
+ See
+ documentation
+ for more information.
+
+
+ Configurable Quote type. See Guide for more information.
+ Historical price quotes.
+ Optional. Number of periods in the moving average of ADL.
+ Time series of ADL values.
+ Invalid parameter value provided.
+
+
+
+
+ Directional Movement Index (DMI) and Average Directional Movement Index (ADX) is a measure of price directional movement.
+ It includes upward and downward indicators, and is often used to measure strength of trend.
+
+ See
+ documentation
+ for more information.
+
+
+ Configurable Quote type. See Guide for more information.
+ Historical price quotes.
+ Number of periods in the lookback window.
+ Time series of ADX and Plus/Minus Directional values.
+ Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+ Indicator
+ results to evaluate.
+ Time
+ series of results, pruned.
+
+
+
+
+ Williams Alligator is an indicator that transposes multiple moving averages,
+ showing chart patterns that creator Bill Williams compared to an alligator's
+ feeding habits when describing market movement.
+
+ See
+ documentation
+ for more information.
+
+
+ Configurable Quote type. See Guide for more information.
+ Historical price quotes.
+ Lookback periods for the Jaw line.
+ Offset periods for the Jaw line.
+ Lookback periods for the Teeth line.
+ Offset periods for the Teeth line.
+ Lookback periods for the Lips line.
+ Offset periods for the Lips line.
+ Time series of Alligator values.
+ Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+ Indicator results to evaluate.
+ Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Arnaud Legoux Moving Average (ALMA) is a Gaussian distribution
+ weighted moving average of price over a lookback window.
+
+ See
+ documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Adjusts smoothness versus responsiveness.
+Defines the width of the Gaussian normal distribution.
+Time series of ALMA values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Aroon is a simple oscillator view of how long the new high or low price occured over a lookback window.
+
+ See
+ documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Aroon Up/Down and Oscillator values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Average True Range (ATR) is a measure of volatility that captures gaps and limits between periods.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of ATR values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ ATR Trailing Stop attempts to determine the primary trend of prices by using
+ Average True Range (ATR) band thresholds. It can indicate a buy/sell signal or a
+ trailing stop when the trend changes.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for ATR.
+Multiplier sets the ATR band width.
+Sets basis for stop offsets (Close or High/Low).
+Time series of ATR Trailing Stop values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Awesome Oscillator (aka Super AO) is a measure of the gap between a fast and slow period modified moving average.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the Fast moving average.
+Number of periods in the Slow moving average.
+Time series of Awesome Oscillator values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ A simple quote transform.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+The OHLCV element or simply calculated value type.
+Time series of Basic Quote values.
+Invalid candle part provided.
+
+
+
+
+ Beta shows how strongly one stock responds to systemic volatility of the entire market.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes for Evaluation.
+Historical price quotes for Market.
+Number of periods in the lookback window.
+Type of Beta to calculate.
+Time series of Beta values.
+Invalid parameter value provided.
+Invalid quotes provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Bollinger Bands® depict volatility as standard deviation boundary lines from a moving average of price.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Width of bands. Number of Standard Deviations from the moving average.
+Time series of Bollinger Band and %B values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Balance of Power (aka Balance of Market Power) is a momentum oscillator that depicts the strength of buying and selling pressure.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for smoothing.
+Time series of BOP values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Commodity Channel Index (CCI) is an oscillator depicting deviation from typical price range, often used to identify cyclical trends.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of CCI values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Chaikin Oscillator is the difference between fast and slow Exponential Moving Averages (EMA) of the Accumulation/Distribution Line (ADL).
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the ADL fast EMA.
+Number of periods for the ADL slow EMA.
+Time series of Chaikin Oscillator, Money Flow Volume, and ADL values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Chandelier Exit is typically used for stop-loss and can be computed for both long or short types.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Multiplier.
+Short or Long variant selection.
+Time series of Chandelier Exit values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Choppiness Index (CHOP) measures the trendiness or choppiness over N lookback periods
+ on a scale of 0 to 100.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of CHOP values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Chaikin Money Flow (CMF) is the simple moving average of Money Flow Volume (MFV).
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the MFV moving average.
+Time series of Chaikin Money Flow and MFV values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ The Chande Momentum Oscillator is a momentum indicator depicting the weighted percent of higher prices in financial markets.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of CMO values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ ConnorsRSI is a composite oscillator that incorporates RSI, winning/losing streaks, and percentile gain metrics on scale of 0 to 100.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the RSI.
+Number of periods for streak RSI.
+Number of periods for the percentile ranking.
+Time series of ConnorsRSI, RSI, Streak RSI, and Percent Rank values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Correlation Coefficient between two quote histories, based on price.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes A for comparison.
+Historical price quotes B for comparison.
+Number of periods in the lookback window.
+
+ Time series of Correlation Coefficient values.
+ R², Variance, and Covariance are also included.
+
+Invalid parameter value provided.
+Invalid quotes provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Double Exponential Moving Average (DEMA) of the price.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Double EMA values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Doji is a single candlestick pattern where open and close price are virtually identical, representing market indecision.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Optional. Maximum absolute percent difference in open and close price.
+Time series of Doji values.
+Invalid parameter value provided.
+
+
+
+
+ Doji is a single candlestick pattern where open and close price are virtually identical, representing market indecision.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Optional. Maximum absolute percent difference in open and close price.
+Time series of Doji values.
+Invalid parameter value provided.
+
+
+
+
+ Donchian Channels, also called Price Channels, are derived from highest High and lowest Low values over a lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Donchian Channel values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Detrended Price Oscillator (DPO) depicts the difference between price and an offset simple moving average.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of DPO values.
+Invalid parameter value provided.
+
+
+
+
+ McGinley Dynamic is a more responsive variant of exponential moving average.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Optional. Range adjustment factor.
+Time series of Dynamic values.
+Invalid parameter value provided.
+
+
+
+
+ The Elder-ray Index depicts buying and selling pressure, also known as Bull and Bear Power.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the EMA.
+Time series of Elder-ray Index values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Exponential Moving Average (EMA) of price or any other specified OHLCV element.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of EMA values.
+Invalid parameter value provided.
+
+
+
+
+ Extablish a streaming base for Exponential Moving Average (EMA).
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+EMA base that you can add Quotes to with the .Add(quote) method.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Endpoint Moving Average (EPMA), also known as Least Squares Moving Average (LSMA), plots the projected last point of a linear regression lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Endpoint Moving Average values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Fractal Chaos Bands outline high and low price channels to depict broad less-chaotic price movements. FCB is a channelized depiction of Williams Fractals.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of span periods in the evaluation window.
+Time series of Fractal Chaos Band and Oscillator values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Ehlers Fisher Transform converts prices into a Gaussian normal distribution.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Fisher Transform values.
+Invalid parameter value provided.
+
+
+
+
+ The Force Index depicts volume-based buying and selling pressure.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the EMA of Force Index.
+Time series of Force Index values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Williams Fractal is a retrospective price pattern that identifies a central high or low point over a lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of span periods to the left and right of the evaluation period.
+Determines use of Close or High/Low wicks for points.
+Time series of Williams Fractal Bull/Bear values.
+Invalid parameter value provided.
+
+
+
+
+ Williams Fractal is a retrospective price pattern that identifies a central high or low point over a lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of span periods to the left of the evaluation period.
+Number of span periods to the right of the evaluation period.
+Determines use of Close or High/Low wicks for points.
+Time series of Williams Fractal Bull/Bear values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+
+ Gator Oscillator is an expanded view of Williams Alligator.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Time series of Gator values.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Heikin-Ashi is a modified candlestick pattern that uses prior day for smoothing.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Time series of Heikin-Ashi candlestick values.
+
+
+
+
+ Hull Moving Average (HMA) is a modified weighted average of price over N lookback periods that reduces lag.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of HMA values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Hilbert Transform Instantaneous Trendline (HTL) is a 5-period trendline of high/low price that uses signal processing to reduce noise.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Time series of HTL values and smoothed price.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Hurst Exponent is a measure of randomness, trending, and mean-reverting tendencies of incremental return values.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of lookback periods.
+Time series of Hurst Exponent values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Ichimoku Cloud, also known as Ichimoku Kinkō Hyō, is a collection of indicators that depict support and resistance, momentum, and trend direction.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the Tenkan-sen midpoint evaluation.
+Number of periods in the shorter Kijun-sen midpoint evaluation. This value is also used to offset Senkou and Chinkou spans.
+Number of periods in the longer Senkou leading span B midpoint evaluation.
+Time series of Ichimoku Cloud values.
+Invalid parameter value provided.
+
+
+
+
+ Ichimoku Cloud, also known as Ichimoku Kinkō Hyō, is a collection of indicators that depict support and resistance, momentum, and trend direction.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the Tenkan-sen midpoint evaluation.
+Number of periods in the shorter Kijun-sen midpoint evaluation.
+Number of periods in the longer Senkou leading span B midpoint evaluation.
+Number of periods to displace the Senkou and Chikou Spans.
+Time series of Ichimoku Cloud values.
+Invalid parameter value provided.
+
+
+
+
+ Ichimoku Cloud, also known as Ichimoku Kinkō Hyō, is a collection of indicators that depict support and resistance, momentum, and trend direction.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the Tenkan-sen midpoint evaluation.
+Number of periods in the shorter Kijun-sen midpoint evaluation.
+Number of periods in the longer Senkou leading span B midpoint evaluation.
+Number of periods to displace the Senkou Spans.
+Number of periods in displace the Chikou Span.
+Time series of Ichimoku Cloud values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+
+ Kaufman’s Adaptive Moving Average (KAMA) is an volatility adaptive moving average of price over configurable lookback periods.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of Efficiency Ratio (volatility) periods.
+Number of periods in the Fast EMA.
+Number of periods in the Slow EMA.
+Time series of KAMA values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Keltner Channels are based on an EMA centerline and ATR band widths. See also STARC Bands for an SMA centerline equivalent.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the centerline EMA.
+ATR multiplier sets the width of the channel.
+Number of periods in the ATR evaluation.
+Time series of Keltner Channel values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Klinger Oscillator depicts volume-based divergence between short and long-term money flow.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the short EMA.
+Number of periods for the long EMA.
+Number of periods Signal line.
+Time series of Klinger Oscillator values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Moving Average Convergence/Divergence (MACD) is a simple oscillator view of two converging/diverging exponential moving averages.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the Fast EMA.
+Number of periods in the Slow EMA.
+Number of periods for the Signal moving average.
+Time series of MACD values, including MACD, Signal, and Histogram.
+Invalid parameter value provided.
+
+
+
+
+ Moving Average Envelopes is a price band overlay that is offset from the moving average of price over a lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Percent offset for envelope width.
+Moving average type (e.g. EMA, HMA, TEMA, etc.).
+Time series of MA Envelopes values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+
+ MESA Adaptive Moving Average (MAMA) is a 5-period adaptive moving average of high/low price.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Fast limit threshold.
+Slow limit threshold.
+Time series of MAMA values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Marubozu is a single candlestick pattern that has no wicks, representing consistent directional movement.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Optional. Minimum candle body size as percentage.
+Time series of Marubozu values.
+Invalid parameter value provided.
+
+
+
+
+ Marubozu is a single candlestick pattern that has no wicks, representing consistent directional movement.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Optional. Minimum candle body size as percentage.
+Time series of Marubozu values.
+Invalid parameter value provided.
+
+
+
+
+ Money Flow Index (MFI) is a price-volume oscillator that shows buying and selling momentum.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of MFI values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ On-balance Volume (OBV) is a rolling accumulation of volume based on Close price direction.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Optional. Number of periods for an SMA of the OBV line.
+Time series of OBV values.
+Invalid parameter value provided.
+
+
+
+
+ Parabolic SAR (stop and reverse) is a price-time based indicator used to determine trend direction and reversals.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Incremental step size.
+Maximum step threshold.
+Time series of Parabolic SAR values.
+Invalid parameter value provided.
+
+
+
+
+ Parabolic SAR (stop and reverse) is a price-time based indicator used to determine trend direction and reversals.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Incremental step size.
+Maximum step threshold.
+Initial starting acceleration factor.
+Time series of Parabolic SAR values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Pivot Points depict support and resistance levels, based on the prior lookback window. You can specify window size (e.g. month, week, day, etc).
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Calendar size of the lookback window.
+Pivot Point type.
+Time series of Pivot Points values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Pivots is an extended version of Williams Fractal that includes identification of Higher High, Lower Low, Higher Low, and Lower Low trends between pivots in a lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of span periods to the left of the evaluation period.
+Number of span periods to the right of the evaluation period.
+Number of periods in the lookback window.
+Determines use of Close or High/Low wicks for points.
+Time series of Pivots values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+
+ Price Momentum Oscillator (PMO) is double-smoothed ROC based momentum indicator.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for ROC EMA smoothing.
+Number of periods for PMO EMA smoothing.
+Number of periods for Signal line EMA.
+Time series of PMO values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Price Relative Strength (PRS), also called Comparative Relative Strength,
+ shows the ratio of two quote histories. It is often used to compare
+ against a market index or sector ETF. When using the optional lookbackPeriods,
+ this also return relative percent change over the specified periods.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes for evaluation.
+This is usually market index data, but could be any baseline data that you might use for comparison.
+Optional. Number of periods for % difference.
+Optional. Number of periods for a PRS SMA signal line.
+Time series of PRS values.
+Invalid parameter value provided.
+Invalid quotes provided.
+
+
+
+
+ Percentage Volume Oscillator (PVO) is a simple oscillator view of two converging/diverging exponential moving averages of Volume.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the Fast moving average.
+Number of periods in the Slow moving average.
+Number of periods for the PVO SMA signal line.
+Time series of PVO values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Renko Chart is a modified Japanese candlestick pattern that uses time-lapsed bricks.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Fixed brick size ($).
+End type. See documentation.
+Time series of Renko Chart candlestick values.
+Invalid parameter value provided.
+
+
+
+
+ The ATR Renko Chart is a modified Japanese candlestick pattern based on Average True Range brick size.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Lookback periods for the ATR evaluation.
+End type. See documentation.
+Time series of Renko Chart candlestick values.
+
+
+
+
+ Rate of Change (ROC), also known as Momentum Oscillator, is the percent change of price over a lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Optional. Number of periods for an ROC SMA signal line.
+Time series of ROC values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Rate of Change with Bands (ROCWB) is the percent change of price over a lookback window with standard deviation bands.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Number of periods for the ROC EMA line.
+Number of periods the standard deviation for upper/lower band lines.
+Time series of ROCWB values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Rolling Pivot Points is a modern update to traditional fixed calendar window Pivot Points.
+ It depicts support and resistance levels, based on a defined rolling window and offset.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the evaluation window.
+Number of periods to offset the window from the current period.
+Pivot Point type.
+Time series of Rolling Pivot Points values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Relative Strength Index (RSI) measures strength of the winning/losing streak over N lookback periods
+ on a scale of 0 to 100, to depict overbought and oversold conditions.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of RSI values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Slope of the best fit line is determined by an ordinary least-squares simple linear regression on price.
+ It can be used to help identify trend strength and direction.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Slope values, including Slope, Standard Deviation, R², and a best-fit Line (for the last lookback segment).
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Simple Moving Average (SMA) of the price.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of SMA values.
+Invalid parameter value provided.
+
+
+
+
+ Simple Moving Average (SMA) is the average of price over a lookback window. This extended variant includes mean absolute deviation (MAD), mean square error (MSE), and mean absolute percentage error (MAPE).
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of SMA, MAD, MSE, and MAPE values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Stochastic Momentum Index is a double-smoothed variant of the Stochastic Oscillator on a scale from -100 to 100.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the Stochastic lookback.
+Number of periods in the first smoothing.
+Number of periods in the second smoothing.
+Number of periods in the EMA of SMI.
+Time series of Stochastic Momentum Index values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Smoothed Moving Average (SMMA) is the average of price over a lookback window using a smoothing method.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of SMMA values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Stoller Average Range Channel (STARC) Bands, are based on an SMA centerline and ATR band widths.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the centerline SMA.
+ATR multiplier sets the width of the channel.
+Number of periods in the ATR evaluation.
+Time series of STARC Bands values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Schaff Trend Cycle is a stochastic oscillator view of two converging/diverging exponential moving averages.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the Trend Cycle.
+Number of periods in the Fast EMA.
+Number of periods in the Slow EMA.
+Time series of MACD values, including MACD, Signal, and Histogram.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Rolling Standard Deviation of price over a lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Optional. Number of periods in the Standard Deviation SMA signal line.
+Time series of Standard Deviations values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Standard Deviation Channels are based on an linear regression centerline and standard deviations band widths.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Size of the evaluation window.
+Width of bands. Number of Standard Deviations from the regression line.
+Time series of Standard Deviation Channels values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Stochastic Oscillator is a momentum indicator that looks back N periods to produce a scale of 0 to 100.
+ %J is also included for the KDJ Index extension.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the Oscillator.
+Smoothing period for the %D signal line.
+Smoothing period for the %K Oscillator. Use 3 for Slow or 1 for Fast.
+Time series of Stochastic Oscillator values.
+Invalid parameter value provided.
+
+
+
+
+ Stochastic Oscillator is a momentum indicator that looks back N periods to produce a scale of 0 to 100.
+ %J is also included for the KDJ Index extension.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the Oscillator.
+Smoothing period for the %D signal line.
+Smoothing period for the %K Oscillator. Use 3 for Slow or 1 for Fast.
+Weight of %K in the %J calculation. Default is 3.
+Weight of %K in the %J calculation. Default is 2.
+Type of moving average to use. Default is MaType.SMA. See docs for instructions and options.
+Time series of Stochastic Oscillator values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Stochastic RSI is a Stochastic interpretation of the Relative Strength Index.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the RSI.
+Number of periods for the Stochastic.
+Number of periods for the Stochastic RSI SMA signal line.
+Number of periods for Stochastic Smoothing. Use 1 for Fast or 3 for Slow.
+Time series of Stochastic RSI values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ SuperTrend attempts to determine the primary trend of prices by using
+ Average True Range (ATR) band thresholds around an HL2 midline. It can indicate a buy/sell signal or a
+ trailing stop when the trend changes.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for ATR.
+Multiplier sets the ATR band width.
+Time series of SuperTrend values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Tillson T3 is a smooth moving average that reduces both lag and overshooting.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the EMA smoothing.
+Size of the Volume Factor.
+Time series of T3 values.
+Invalid parameter value provided.
+
+
+
+
+ Triple Exponential Moving Average (TEMA) of the price. Note: TEMA is often confused with the alternative TRIX oscillator.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Triple EMA values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ True Range (TR) is a measure of volatility that captures gaps and limits between periods.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Time series of True Range (TR) values.
+Invalid parameter value provided.
+
+
+
+
+ Triple EMA Oscillator (TRIX) is the rate of change for a 3 EMA smoothing of the price over a lookback window. TRIX is often confused with TEMA.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Optional. Number of periods for a TRIX SMA signal line.
+Time series of TRIX values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ True Strength Index (TSI) is a momentum oscillator that depicts trends in price changes.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods for the first EMA.
+Number of periods in the second smoothing.
+Number of periods in the TSI SMA signal line.
+Time series of TSI values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Ulcer Index (UI) is a measure of downside price volatility over a lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Ulcer Index values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Ultimate Oscillator uses several lookback periods to weigh buying power against True Range price to produce on oversold / overbought oscillator.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the smallest window.
+Number of periods in the middle-sized window.
+Number of periods in the largest window.
+Time series of Ultimate Oscillator values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Volatility Stop is an ATR based indicator used to determine trend direction, stops, and reversals.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+ATR offset amount.
+Time series of Volatility Stop values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Vortex Indicator (VI) is a measure of price directional movement.
+ It includes positive and negative indicators, and is often used to identify trends and reversals.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of VI+ and VI- vortex movement indicator values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Volume Weighted Average Price (VWAP) is a Volume weighted average of price, typically used on intraday data.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Optional anchor date. If not provided, the first date in quotes is used.
+Time series of VWAP values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Volume Weighted Moving Average is the volume adjusted average price over a lookback window.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Volume Weighted Moving Average values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Williams %R momentum indicator is a stochastic oscillator with scale of -100 to 0. It is exactly the same as the Fast variant of Stochastic Oscillator, but with a different scaling.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of Williams %R values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Weighted Moving Average (WMA) is the linear weighted average of price over N lookback periods. This also called Linear Weighted Moving Average (LWMA).
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Number of periods in the lookback window.
+Time series of WMA values.
+Invalid parameter value provided.
+
+
+
+ Removes the recommended quantity of results from the beginning of the results list
+ using a reverse-engineering approach. See
+ documentation for more information.
+
+Indicator
+ results to evaluate.
+Time
+ series of results, pruned.
+
+
+
+
+ Zig Zag is a price chart overlay that simplifies the up and down movements and transitions based on a percent change smoothing threshold.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Determines use of Close or High/Low wicks for extreme points.
+Percent price change to set threshold for minimum size movements.
+Time series of Zig Zag values.
+Invalid parameter value provided.
+
+
+
+ Removes non-essential records containing null values with unique consideration for
+ this indicator. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Time series of
+ indicator results, condensed.
+
+
+
+
+ Stochastic indicator results includes aliases for those who prefer the simpler K,D,J outputs.
+
+ See
+documentation
+ for more information.
+
+
+
+ Standard output properties:
+
+-
+Oscillator
+%K Oscillator over prior lookback periods.
+
+-
+Signal
+%D Simple moving average of %K Oscillator.
+
+-
+PercentJ
+
+ %J is the weighted divergence of %K and %D: %J=3×%K-2×%D
+
+
+
+ These are the aliases of the above properties:
+
+-
+K
+Same as Oscillator.
+
+-
+D
+Same as Signal.
+
+-
+J
+Same as PercentJ.
+
+
+
+
+
+
+ Removes a specific quantity from the beginning of the time series list.
+
+ See documentation for more information.
+
+
+Any series type.
+Collection to evaluate.
+Exact quantity to remove from the beginning of the series.
+Time series, pruned.
+Invalid parameter value provided.
+
+
+
+ Finds time series values on a specific date.
+
+ See documentation for more information.
+
+
+Any series type.
+Time series to evaluate.
+Exact date to lookup.
+First
+ record in the series on the date specified.
+
+
+
+
+ Nullable System.
+ functions.
+
+
+System.Math infamously does not allow
+ or handle nullable input values.
+ Instead of adding repetitive inline defensive code,
+ we're using these equivalents. Most are simple wrappers.
+
+
+
+
+ Returns the absolute value of a nullable double.
+
+The nullable double value.
+The absolute value, or null if the input is null.
+
+
+
+ Rounds a nullable decimal value to a specified number of fractional digits.
+
+The nullable decimal value.
+The number of fractional digits.
+The rounded value, or null if the input is null.
+
+
+
+ Rounds a nullable double value to a specified number of fractional digits.
+
+The nullable double value.
+The number of fractional digits.
+The rounded value, or null if the input is null.
+
+
+
+ Rounds a double value to a specified number of fractional digits.
+ It is an extension alias of
+
+The double value.
+The number of fractional digits.
+The rounded value.
+
+
+
+ Rounds a decimal value to a specified number of fractional digits.
+ It is an extension alias of
+
+The decimal value.
+The number of fractional digits.
+The rounded value.
+
+
+
+ Converts a nullable double value to NaN if it is null.
+
+The nullable double value.
+The value, or NaN if the input is null.
+
+
+
+ Converts a nullable decimal value to NaN if it is null.
+
+The nullable decimal value.
+The value as a double, or NaN if the input is null.
+
+
+
+ Converts a nullable double value to null if it is NaN.
+
+The nullable double value.
+The value, or null if the input is NaN.
+
+
+
+ Converts a double value to null if it is NaN.
+
+The double value.
+The value, or null if the input is NaN.
+
+
+
+ Converts historical quotes into larger bar sizes.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+PeriodSize enum representing the new bar size.
+Time series of historical quote values.
+Invalid parameter value provided.
+
+
+
+
+ Converts historical quotes into larger bar sizes.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+TimeSpan representing the new bar size.
+Time series of historical quote values.
+Invalid parameter value provided.
+
+
+
+
+ Optionally select which candle part to use in the calculation.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+The OHLCV element or simply calculated value type.
+Time series of Quote tuple values.
+Invalid candle part provided.
+
+
+
+
+ Validate historical quotes.
+
+ See
+documentation
+ for more information.
+
+
+Configurable Quote type. See Guide for more information.
+Historical price quotes.
+Time series of historical quote values.
+Validation check failed.
+
+
+
+
+ Forces indicator results to have the same date-based records as another result baseline.
+
+ This utility is undocumented.
+
+
+Any indicator result series type to be transformed.
+Any indicator result series type to be matched.
+The indicator result series to be modified.
+The indicator result series to compare for matching.
+Synchronization behavior See options in SyncType enum.
+Indicator result series, synchronized to a comparator match.
+
+ Invalid parameter value provided.
+
+
+
+
+ Removes non-essential records containing null or NaN values. See
+ documentation for more information.
+
+Any result
+ type.
+Indicator results to evaluate.
+Time series of indicator results,
+ condensed.
+
+
+
+Converts results into a reusable tuple with warmup periods removed and nulls converted
+ to NaN. See
+ documentation for more information.
+
+Indicator results to evaluate.
+Collection of non-nullable tuple time series of results, without null warmup periods.
+
+
+
+Converts results into a tuple collection with non-nullable NaN to replace null values.
+ See
+ documentation for more information.
+
+Indicator results to evaluate.
+Collection of tuple time series of
+ results with specified handling of nulls, without pruning.
+
+
+
+
diff --git a/stock_indicators/_cstypes/__init__.py b/stock_indicators/_cstypes/__init__.py
index f2e76b20..d24d596b 100644
--- a/stock_indicators/_cstypes/__init__.py
+++ b/stock_indicators/_cstypes/__init__.py
@@ -2,6 +2,6 @@
from stock_indicators import _cslib
-from .datetime import (DateTime, to_pydatetime)
-from .decimal import (Decimal, to_pydecimal)
-from .list import (List)
+from .datetime import DateTime, to_pydatetime
+from .decimal import Decimal, to_pydecimal
+from .list import List
diff --git a/stock_indicators/_cstypes/datetime.py b/stock_indicators/_cstypes/datetime.py
index 4fd127e0..e3449933 100644
--- a/stock_indicators/_cstypes/datetime.py
+++ b/stock_indicators/_cstypes/datetime.py
@@ -1,5 +1,8 @@
-from datetime import datetime as PyDateTime, timezone as PyTimezone
+from datetime import datetime as PyDateTime
+from datetime import timezone as PyTimezone
+
from System import DateTimeKind # type: ignore
+
from stock_indicators._cslib import CsDateTime # type: ignore
# Module-level constant: 1 second = 10,000,000 ticks (100ns per tick)
diff --git a/stock_indicators/_cstypes/decimal.py b/stock_indicators/_cstypes/decimal.py
index d72972ef..5c72d344 100644
--- a/stock_indicators/_cstypes/decimal.py
+++ b/stock_indicators/_cstypes/decimal.py
@@ -1,6 +1,7 @@
from decimal import Decimal as PyDecimal
+from typing import Optional, Union
-from stock_indicators._cslib import CsDecimal, CsCultureInfo, CsNumberStyles
+from stock_indicators._cslib import CsCultureInfo, CsDecimal, CsNumberStyles
class Decimal:
@@ -8,7 +9,7 @@ class Decimal:
Class for converting a number into C#'s `System.Decimal` class.
Parameters:
- decimal : `int`, `float` or any `object` that can be represented as a number string.
+ decimal : `int`, `float`, `PyDecimal`, or any `object` that can be represented as a number string.
Example:
Constructing `System.Decimal` from `float` of Python.
@@ -17,19 +18,51 @@ class Decimal:
>>> cs_decimal
2.5
"""
- cs_number_styles = CsNumberStyles.AllowDecimalPoint | CsNumberStyles.AllowExponent \
- | CsNumberStyles.AllowLeadingSign | CsNumberStyles.AllowThousands
- def __new__(cls, decimal) -> CsDecimal:
- return CsDecimal.Parse(str(decimal), cls.cs_number_styles, CsCultureInfo.InvariantCulture)
+ cs_number_styles = (
+ CsNumberStyles.AllowDecimalPoint
+ | CsNumberStyles.AllowExponent
+ | CsNumberStyles.AllowLeadingSign
+ | CsNumberStyles.AllowThousands
+ )
+ def __new__(cls, decimal: Union[int, float, PyDecimal, str, None]) -> CsDecimal:
+ if decimal is None:
+ from stock_indicators.exceptions import ValidationError
-def to_pydecimal(cs_decimal: CsDecimal) -> PyDecimal:
+ raise ValidationError("Cannot convert None to C# Decimal")
+
+ # Convert to string first to preserve precision for all numeric types
+ try:
+ return CsDecimal.Parse(
+ str(decimal), cls.cs_number_styles, CsCultureInfo.InvariantCulture
+ )
+ except Exception as e:
+ from stock_indicators.exceptions import TypeConversionError
+
+ raise TypeConversionError(
+ f"Cannot convert {decimal} (type: {type(decimal)}) to C# Decimal: {e}"
+ ) from e
+
+
+def to_pydecimal(cs_decimal: Optional[CsDecimal]) -> Optional[PyDecimal]:
"""
Converts an object to a native Python decimal object.
Parameter:
- cs_decimal : `System.Decimal` of C# or any `object` that can be represented as a number.
+ cs_decimal : `System.Decimal` of C# or None.
+
+ Returns:
+ Python Decimal object or None if input is None.
"""
- if cs_decimal is not None:
+ if cs_decimal is None:
+ return None
+
+ try:
return PyDecimal(cs_decimal.ToString(CsCultureInfo.InvariantCulture))
+ except Exception as e:
+ from stock_indicators.exceptions import TypeConversionError
+
+ raise TypeConversionError(
+ f"Cannot convert C# Decimal to Python Decimal: {e}"
+ ) from e
diff --git a/stock_indicators/_cstypes/list.py b/stock_indicators/_cstypes/list.py
index 9a37b694..4aa8fdbe 100644
--- a/stock_indicators/_cstypes/list.py
+++ b/stock_indicators/_cstypes/list.py
@@ -1,4 +1,5 @@
from collections import deque
+from typing import Iterable
from stock_indicators._cslib import CsList
@@ -30,8 +31,20 @@ class List:
>>> print(i, end='')
123
"""
- def __new__(cls, generic, sequence) -> CsList:
- cs_list = CsList[generic]()
- deque(map(cs_list.Add, sequence), maxlen=0)
- return cs_list
+ def __new__(cls, generic: object, sequence: Iterable) -> CsList:
+ if not hasattr(sequence, "__iter__"):
+ raise TypeError("sequence must be iterable")
+
+ try:
+ cs_list = CsList[generic]()
+
+ # Always use individual Add calls for reliability
+ # AddRange has issues with Python.NET type conversion in some cases
+ deque(map(cs_list.Add, sequence), maxlen=0)
+
+ return cs_list
+ except Exception as e:
+ raise ValueError(
+ f"Cannot convert sequence to C# List[{generic}]: {e}"
+ ) from e
diff --git a/stock_indicators/exceptions.py b/stock_indicators/exceptions.py
new file mode 100644
index 00000000..913f1cf8
--- /dev/null
+++ b/stock_indicators/exceptions.py
@@ -0,0 +1,23 @@
+"""
+Custom exceptions for Stock Indicators for Python.
+"""
+
+
+class StockIndicatorsError(Exception):
+ """Base exception class for all Stock Indicators errors."""
+
+
+class StockIndicatorsInitializationError(ImportError, StockIndicatorsError):
+ """Raised when the .NET library fails to initialize."""
+
+
+class TypeConversionError(StockIndicatorsError):
+ """Raised when conversion between Python and C# types fails."""
+
+
+class IndicatorCalculationError(StockIndicatorsError):
+ """Raised when indicator calculation fails."""
+
+
+class ValidationError(StockIndicatorsError):
+ """Raised when input validation fails."""
diff --git a/stock_indicators/indicators/__init__.py b/stock_indicators/indicators/__init__.py
index d11e5fc6..2a415203 100644
--- a/stock_indicators/indicators/__init__.py
+++ b/stock_indicators/indicators/__init__.py
@@ -2,91 +2,85 @@
from stock_indicators import _cslib
-from .adl import (get_adl)
-from .adx import (get_adx)
-from .alligator import (get_alligator)
-from .alma import (get_alma)
-from .aroon import (get_aroon)
-from .atr_stop import (get_atr_stop)
-from .atr import (get_atr)
-from .awesome import (get_awesome)
-from .basic_quotes import (get_basic_quote)
-from .beta import (get_beta)
-from .bollinger_bands import (get_bollinger_bands)
-from .bop import (get_bop)
-from .cci import (get_cci)
-from .chaikin_oscillator import (get_chaikin_osc)
-from .chandelier import (get_chandelier)
-from .chop import (get_chop)
-from .cmf import (get_cmf)
-from .cmo import (get_cmo)
-from .connors_rsi import (get_connors_rsi)
-from .correlation import (get_correlation)
-from .doji import (get_doji)
-from .donchian import (get_donchian)
-from .dema import (get_dema)
-from .dpo import (get_dpo)
-from .dynamic import (get_dynamic)
-from .elder_ray import (get_elder_ray)
-from .ema import (get_ema)
-from .epma import (get_epma)
-from .fcb import (get_fcb)
-from .fisher_transform import (get_fisher_transform)
-from .force_index import (get_force_index)
-from .fractal import (get_fractal)
-from .gator import (get_gator)
-from .heikin_ashi import (get_heikin_ashi)
-from .hma import (get_hma)
-from .ht_trendline import (get_ht_trendline)
-from .hurst import (get_hurst)
-from .ichimoku import (get_ichimoku)
-from .kama import (get_kama)
-from .keltner import (get_keltner)
-from .kvo import (get_kvo)
-from .macd import (get_macd)
-from .ma_envelopes import (get_ma_envelopes)
-from .mama import (get_mama)
-from .marubozu import (get_marubozu)
-from .mfi import (get_mfi)
-from .obv import (get_obv)
-from .parabolic_sar import (get_parabolic_sar)
-from .pivot_points import (get_pivot_points)
-from .pivots import (get_pivots)
-from .pmo import (get_pmo)
-from .prs import (get_prs)
-from .pvo import (get_pvo)
-from .renko import (
- get_renko,
- get_renko_atr)
-from .roc import (
- get_roc,
- get_roc_with_band)
-from .rolling_pivots import (get_rolling_pivots)
-from .rsi import (get_rsi)
-from .slope import (get_slope)
-from .sma import (
- get_sma,
- get_sma_analysis)
-from .smi import (get_smi)
-from .smma import (get_smma)
-from .starc_bands import (get_starc_bands)
-from .stc import (get_stc)
-from .stdev_channels import (get_stdev_channels)
-from .stdev import (get_stdev)
-from .stoch import (get_stoch)
-from .stoch_rsi import (get_stoch_rsi)
-from .super_trend import (get_super_trend)
-from .t3 import (get_t3)
-from .tema import (get_tema)
-from .tr import (get_tr)
-from .trix import (get_trix)
-from .tsi import (get_tsi)
-from .ulcer_index import (get_ulcer_index)
-from .ultimate import (get_ultimate)
-from .volatility_stop import (get_volatility_stop)
-from .vortex import (get_vortex)
-from .vwap import (get_vwap)
-from .vwma import (get_vwma)
-from .williams_r import (get_williams_r)
-from .wma import (get_wma)
-from .zig_zag import (get_zig_zag)
+from .adl import get_adl
+from .adx import get_adx
+from .alligator import get_alligator
+from .alma import get_alma
+from .aroon import get_aroon
+from .atr import get_atr
+from .atr_stop import get_atr_stop
+from .awesome import get_awesome
+from .basic_quotes import get_basic_quote
+from .beta import get_beta
+from .bollinger_bands import get_bollinger_bands
+from .bop import get_bop
+from .cci import get_cci
+from .chaikin_oscillator import get_chaikin_osc
+from .chandelier import get_chandelier
+from .chop import get_chop
+from .cmf import get_cmf
+from .cmo import get_cmo
+from .connors_rsi import get_connors_rsi
+from .correlation import get_correlation
+from .dema import get_dema
+from .doji import get_doji
+from .donchian import get_donchian
+from .dpo import get_dpo
+from .dynamic import get_dynamic
+from .elder_ray import get_elder_ray
+from .ema import get_ema
+from .epma import get_epma
+from .fcb import get_fcb
+from .fisher_transform import get_fisher_transform
+from .force_index import get_force_index
+from .fractal import get_fractal
+from .gator import get_gator
+from .heikin_ashi import get_heikin_ashi
+from .hma import get_hma
+from .ht_trendline import get_ht_trendline
+from .hurst import get_hurst
+from .ichimoku import get_ichimoku
+from .kama import get_kama
+from .keltner import get_keltner
+from .kvo import get_kvo
+from .ma_envelopes import get_ma_envelopes
+from .macd import get_macd
+from .mama import get_mama
+from .marubozu import get_marubozu
+from .mfi import get_mfi
+from .obv import get_obv
+from .parabolic_sar import get_parabolic_sar
+from .pivot_points import get_pivot_points
+from .pivots import get_pivots
+from .pmo import get_pmo
+from .prs import get_prs
+from .pvo import get_pvo
+from .renko import get_renko, get_renko_atr
+from .roc import get_roc, get_roc_with_band
+from .rolling_pivots import get_rolling_pivots
+from .rsi import get_rsi
+from .slope import get_slope
+from .sma import get_sma, get_sma_analysis
+from .smi import get_smi
+from .smma import get_smma
+from .starc_bands import get_starc_bands
+from .stc import get_stc
+from .stdev import get_stdev
+from .stdev_channels import get_stdev_channels
+from .stoch import get_stoch
+from .stoch_rsi import get_stoch_rsi
+from .super_trend import get_super_trend
+from .t3 import get_t3
+from .tema import get_tema
+from .tr import get_tr
+from .trix import get_trix
+from .tsi import get_tsi
+from .ulcer_index import get_ulcer_index
+from .ultimate import get_ultimate
+from .volatility_stop import get_volatility_stop
+from .vortex import get_vortex
+from .vwap import get_vwap
+from .vwma import get_vwma
+from .williams_r import get_williams_r
+from .wma import get_wma
+from .zig_zag import get_zig_zag
diff --git a/stock_indicators/indicators/adl.py b/stock_indicators/indicators/adl.py
index 468b6d3a..0b36548a 100644
--- a/stock_indicators/indicators/adl.py
+++ b/stock_indicators/indicators/adl.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_adl(quotes: Iterable[Quote], sma_periods: Optional[int] = None):
@@ -70,6 +70,8 @@ def adl_sma(self, value):
_T = TypeVar("_T", bound=ADLResult)
+
+
class ADLResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of ADL(Accumulation/Distribution Line) results.
diff --git a/stock_indicators/indicators/adx.py b/stock_indicators/indicators/adx.py
index c7d6639b..a6755cdd 100644
--- a/stock_indicators/indicators/adx.py
+++ b/stock_indicators/indicators/adx.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_adx(quotes: Iterable[Quote], lookback_periods: int = 14):
@@ -53,6 +53,14 @@ def mdi(self) -> Optional[float]:
def mdi(self, value):
self._csdata.Mdi = value
+ @property
+ def dx(self) -> Optional[float]:
+ return self._csdata.Dx
+
+ @dx.setter
+ def dx(self, value):
+ self._csdata.Dx = value
+
@property
def adx(self) -> Optional[float]:
return self._csdata.Adx
@@ -71,6 +79,8 @@ def adxr(self, value):
_T = TypeVar("_T", bound=ADXResult)
+
+
class ADXResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of ADX(Average Directional Movement Index) results.
diff --git a/stock_indicators/indicators/alligator.py b/stock_indicators/indicators/alligator.py
index e0aaea70..74512699 100644
--- a/stock_indicators/indicators/alligator.py
+++ b/stock_indicators/indicators/alligator.py
@@ -3,14 +3,19 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_alligator(quotes: Iterable[Quote],
- jaw_periods: int = 13, jaw_offset: int = 8,
- teeth_periods: int = 8, teeth_offset: int = 5,
- lips_periods: int = 5, lips_offset: int = 3):
+def get_alligator(
+ quotes: Iterable[Quote], # pylint: disable=too-many-positional-arguments
+ jaw_periods: int = 13,
+ jaw_offset: int = 8,
+ teeth_periods: int = 8,
+ teeth_offset: int = 5,
+ lips_periods: int = 5,
+ lips_offset: int = 3,
+):
"""Get Williams Alligator calculated.
Williams Alligator is an indicator that transposes multiple moving averages,
@@ -47,10 +52,15 @@ def get_alligator(quotes: Iterable[Quote],
- [Williams Alligator Reference](https://python.stockindicators.dev/indicators/Alligator/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- alligator_results = CsIndicator.GetAlligator[Quote](CsList(Quote, quotes),
- jaw_periods, jaw_offset,
- teeth_periods, teeth_offset,
- lips_periods, lips_offset)
+ alligator_results = CsIndicator.GetAlligator[Quote](
+ CsList(Quote, quotes),
+ jaw_periods,
+ jaw_offset,
+ teeth_periods,
+ teeth_offset,
+ lips_periods,
+ lips_offset,
+ )
return AlligatorResults(alligator_results, AlligatorResult)
@@ -85,6 +95,8 @@ def lips(self, value):
_T = TypeVar("_T", bound=AlligatorResult)
+
+
class AlligatorResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Williams Alligator results.
diff --git a/stock_indicators/indicators/alma.py b/stock_indicators/indicators/alma.py
index 262c7112..b0ec5366 100644
--- a/stock_indicators/indicators/alma.py
+++ b/stock_indicators/indicators/alma.py
@@ -7,7 +7,12 @@
from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_alma(quotes: Iterable[Quote], lookback_periods: int = 9, offset: float = .85, sigma : float = 6):
+def get_alma(
+ quotes: Iterable[Quote],
+ lookback_periods: int = 9,
+ offset: float = 0.85,
+ sigma: float = 6,
+):
"""Get ALMA calculated.
Arnaud Legoux Moving Average (ALMA) is a Gaussian distribution
@@ -34,7 +39,9 @@ def get_alma(quotes: Iterable[Quote], lookback_periods: int = 9, offset: float =
- [ALMA Reference](https://python.stockindicators.dev/indicators/Alma/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- alma_results = CsIndicator.GetAlma[Quote](CsList(Quote, quotes), lookback_periods, offset, sigma)
+ alma_results = CsIndicator.GetAlma[Quote](
+ CsList(Quote, quotes), lookback_periods, offset, sigma
+ )
return ALMAResults(alma_results, ALMAResult)
@@ -53,6 +60,8 @@ def alma(self, value):
_T = TypeVar("_T", bound=ALMAResult)
+
+
class ALMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of ALMA(Arnaud Legoux Moving Average) results.
diff --git a/stock_indicators/indicators/aroon.py b/stock_indicators/indicators/aroon.py
index 4cf04184..a64e0cd1 100644
--- a/stock_indicators/indicators/aroon.py
+++ b/stock_indicators/indicators/aroon.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_aroon(quotes: Iterable[Quote], lookback_periods: int = 25):
@@ -62,6 +62,8 @@ def oscillator(self, value):
_T = TypeVar("_T", bound=AroonResult)
+
+
class AroonResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Aroon results.
diff --git a/stock_indicators/indicators/atr.py b/stock_indicators/indicators/atr.py
index 6e97978c..e029e57b 100644
--- a/stock_indicators/indicators/atr.py
+++ b/stock_indicators/indicators/atr.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_atr(quotes: Iterable[Quote], lookback_periods: int = 14):
@@ -62,6 +62,8 @@ def atrp(self, value):
_T = TypeVar("_T", bound=ATRResult)
+
+
class ATRResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of ATR(Average True Range) results.
diff --git a/stock_indicators/indicators/atr_stop.py b/stock_indicators/indicators/atr_stop.py
index 140b1774..8e3a8a7f 100644
--- a/stock_indicators/indicators/atr_stop.py
+++ b/stock_indicators/indicators/atr_stop.py
@@ -2,17 +2,21 @@
from typing import Iterable, Optional, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.enums import EndType
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_atr_stop(quotes: Iterable[Quote], lookback_periods: int = 21,
- multiplier: float = 3, end_type: EndType = EndType.CLOSE):
+def get_atr_stop(
+ quotes: Iterable[Quote],
+ lookback_periods: int = 21,
+ multiplier: float = 3,
+ end_type: EndType = EndType.CLOSE,
+):
"""Get ATR Trailing Stop calculated.
ATR Trailing Stop attempts to determine the primary trend of prices by using
@@ -40,7 +44,9 @@ def get_atr_stop(quotes: Iterable[Quote], lookback_periods: int = 21,
- [ATR Trailing Stop Reference](https://python.stockindicators.dev/indicators/AtrStop/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetAtrStop[Quote](CsList(Quote, quotes), lookback_periods, multiplier, end_type.cs_value)
+ results = CsIndicator.GetAtrStop[Quote](
+ CsList(Quote, quotes), lookback_periods, multiplier, end_type.cs_value
+ )
return AtrStopResults(results, AtrStopResult)
@@ -75,6 +81,8 @@ def sell_stop(self, value):
_T = TypeVar("_T", bound=AtrStopResult)
+
+
class AtrStopResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of ATR Trailing Stop results.
diff --git a/stock_indicators/indicators/awesome.py b/stock_indicators/indicators/awesome.py
index 8873d29c..6877790f 100644
--- a/stock_indicators/indicators/awesome.py
+++ b/stock_indicators/indicators/awesome.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_awesome(quotes: Iterable[Quote], fast_periods: int = 5, slow_periods: int = 34):
@@ -31,7 +31,9 @@ def get_awesome(quotes: Iterable[Quote], fast_periods: int = 5, slow_periods: in
- [Awesome Oscillator Reference](https://python.stockindicators.dev/indicators/Awesome/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- awesome_results = CsIndicator.GetAwesome[Quote](CsList(Quote, quotes), fast_periods, slow_periods)
+ awesome_results = CsIndicator.GetAwesome[Quote](
+ CsList(Quote, quotes), fast_periods, slow_periods
+ )
return AwesomeResults(awesome_results, AwesomeResult)
@@ -58,6 +60,8 @@ def normalized(self, value):
_T = TypeVar("_T", bound=AwesomeResult)
+
+
class AwesomeResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Awesome Oscillator (aka Super AO) results.
diff --git a/stock_indicators/indicators/basic_quotes.py b/stock_indicators/indicators/basic_quotes.py
index 5480c1d7..365a8b39 100644
--- a/stock_indicators/indicators/basic_quotes.py
+++ b/stock_indicators/indicators/basic_quotes.py
@@ -3,11 +3,13 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.enums import CandlePart
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_basic_quote(quotes: Iterable[Quote], candle_part: CandlePart = CandlePart.CLOSE):
+def get_basic_quote(
+ quotes: Iterable[Quote], candle_part: CandlePart = CandlePart.CLOSE
+):
"""Get Basic Quote calculated.
A simple quote transform (e.g. HL2, OHL3, etc.) and isolation of individual
@@ -28,7 +30,9 @@ def get_basic_quote(quotes: Iterable[Quote], candle_part: CandlePart = CandlePar
- [Basic Quote Reference](https://python.stockindicators.dev/indicators/BasicQuote/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetBaseQuote[Quote](CsList(Quote, quotes), candle_part.cs_value)
+ results = CsIndicator.GetBaseQuote[Quote](
+ CsList(Quote, quotes), candle_part.cs_value
+ )
return BasicQuoteResults(results, BasicQuoteResult)
@@ -47,6 +51,8 @@ def jaw(self, value):
_T = TypeVar("_T", bound=BasicQuoteResult)
+
+
class BasicQuoteResults(IndicatorResults[_T]):
"""
A wrapper class for the list of Basic Quote results.
diff --git a/stock_indicators/indicators/beta.py b/stock_indicators/indicators/beta.py
index 3e317b19..ec2640eb 100644
--- a/stock_indicators/indicators/beta.py
+++ b/stock_indicators/indicators/beta.py
@@ -4,12 +4,16 @@
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.enums import BetaType
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_beta(eval_quotes: Iterable[Quote], market_quotes: Iterable[Quote],
- lookback_periods: int, beta_type: BetaType = BetaType.STANDARD):
+def get_beta(
+ eval_quotes: Iterable[Quote],
+ market_quotes: Iterable[Quote],
+ lookback_periods: int,
+ beta_type: BetaType = BetaType.STANDARD,
+):
"""Get Beta calculated.
Beta shows how strongly one stock responds to systemic volatility of the entire market.
@@ -36,8 +40,12 @@ def get_beta(eval_quotes: Iterable[Quote], market_quotes: Iterable[Quote],
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- beta_results = CsIndicator.GetBeta[Quote](CsList(Quote, eval_quotes), CsList(Quote, market_quotes),
- lookback_periods, beta_type.cs_value)
+ beta_results = CsIndicator.GetBeta[Quote](
+ CsList(Quote, eval_quotes),
+ CsList(Quote, market_quotes),
+ lookback_periods,
+ beta_type.cs_value,
+ )
return BetaResults(beta_results, BetaResult)
diff --git a/stock_indicators/indicators/bollinger_bands.py b/stock_indicators/indicators/bollinger_bands.py
index 9a955ce5..b2b99158 100644
--- a/stock_indicators/indicators/bollinger_bands.py
+++ b/stock_indicators/indicators/bollinger_bands.py
@@ -3,11 +3,13 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_bollinger_bands(quotes: Iterable[Quote], lookback_periods: int = 20, standard_deviations: float = 2):
+def get_bollinger_bands(
+ quotes: Iterable[Quote], lookback_periods: int = 20, standard_deviations: float = 2
+):
"""Get Bollinger Bands® calculated.
Bollinger Bands® depict volatility as standard deviation
@@ -31,7 +33,9 @@ def get_bollinger_bands(quotes: Iterable[Quote], lookback_periods: int = 20, sta
- [Bollinger Bands® Reference](https://python.stockindicators.dev/indicators/BollingerBands/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- bollinger_bands_results = CsIndicator.GetBollingerBands[Quote](CsList(Quote, quotes), lookback_periods, standard_deviations)
+ bollinger_bands_results = CsIndicator.GetBollingerBands[Quote](
+ CsList(Quote, quotes), lookback_periods, standard_deviations
+ )
return BollingerBandsResults(bollinger_bands_results, BollingerBandsResult)
@@ -90,6 +94,8 @@ def width(self, value):
_T = TypeVar("_T", bound=BollingerBandsResult)
+
+
class BollingerBandsResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Bollinger Bands results.
diff --git a/stock_indicators/indicators/bop.py b/stock_indicators/indicators/bop.py
index 69d3f744..bda166e3 100644
--- a/stock_indicators/indicators/bop.py
+++ b/stock_indicators/indicators/bop.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_bop(quotes: Iterable[Quote], smooth_periods: int = 14):
@@ -48,6 +48,8 @@ def bop(self, value):
_T = TypeVar("_T", bound=BOPResult)
+
+
class BOPResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Balance of Power (aka Balance of Market Power) results.
diff --git a/stock_indicators/indicators/cci.py b/stock_indicators/indicators/cci.py
index f6e151ba..2f8b752a 100644
--- a/stock_indicators/indicators/cci.py
+++ b/stock_indicators/indicators/cci.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_cci(quotes: Iterable[Quote], lookback_periods: int = 20):
@@ -47,6 +47,8 @@ def cci(self, value):
_T = TypeVar("_T", bound=CCIResult)
+
+
class CCIResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Commodity Channel Index (CCI) results.
diff --git a/stock_indicators/indicators/chaikin_oscillator.py b/stock_indicators/indicators/chaikin_oscillator.py
index 394467ad..3b879265 100644
--- a/stock_indicators/indicators/chaikin_oscillator.py
+++ b/stock_indicators/indicators/chaikin_oscillator.py
@@ -3,11 +3,13 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_chaikin_osc(quotes: Iterable[Quote], fast_periods: int = 3, slow_periods: int = 10):
+def get_chaikin_osc(
+ quotes: Iterable[Quote], fast_periods: int = 3, slow_periods: int = 10
+):
"""Get Chaikin Oscillator calculated.
Chaikin Oscillator is the difference between fast and slow
@@ -31,7 +33,9 @@ def get_chaikin_osc(quotes: Iterable[Quote], fast_periods: int = 3, slow_periods
- [Chaikin Oscillator Reference](https://python.stockindicators.dev/indicators/ChaikinOsc/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetChaikinOsc[Quote](CsList(Quote, quotes), fast_periods, slow_periods)
+ results = CsIndicator.GetChaikinOsc[Quote](
+ CsList(Quote, quotes), fast_periods, slow_periods
+ )
return ChaikinOscResults(results, ChaikinOscResult)
@@ -74,6 +78,8 @@ def oscillator(self, value):
_T = TypeVar("_T", bound=ChaikinOscResult)
+
+
class ChaikinOscResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Chaikin Oscillator results.
diff --git a/stock_indicators/indicators/chandelier.py b/stock_indicators/indicators/chandelier.py
index b136f231..55eef624 100644
--- a/stock_indicators/indicators/chandelier.py
+++ b/stock_indicators/indicators/chandelier.py
@@ -4,12 +4,16 @@
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.enums import ChandelierType
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_chandelier(quotes: Iterable[Quote], lookback_periods: int = 22,
- multiplier: float = 3, chandelier_type: ChandelierType = ChandelierType.LONG):
+def get_chandelier(
+ quotes: Iterable[Quote],
+ lookback_periods: int = 22,
+ multiplier: float = 3,
+ chandelier_type: ChandelierType = ChandelierType.LONG,
+):
"""Get Chandelier Exit calculated.
Chandelier Exit is typically used for stop-loss and can be
@@ -36,8 +40,9 @@ def get_chandelier(quotes: Iterable[Quote], lookback_periods: int = 22,
- [Chandelier Exit Reference](https://python.stockindicators.dev/indicators/Chandelier/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetChandelier[Quote](CsList(Quote, quotes), lookback_periods,
- multiplier, chandelier_type.cs_value)
+ results = CsIndicator.GetChandelier[Quote](
+ CsList(Quote, quotes), lookback_periods, multiplier, chandelier_type.cs_value
+ )
return ChandelierResults(results, ChandelierResult)
@@ -56,6 +61,8 @@ def chandelier_exit(self, value):
_T = TypeVar("_T", bound=ChandelierResult)
+
+
class ChandelierResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Chandelier Exit results.
diff --git a/stock_indicators/indicators/chop.py b/stock_indicators/indicators/chop.py
index 40bf6048..da12cf3c 100644
--- a/stock_indicators/indicators/chop.py
+++ b/stock_indicators/indicators/chop.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_chop(quotes: Iterable[Quote], lookback_periods: int = 14):
@@ -47,6 +47,8 @@ def chop(self, value):
_T = TypeVar("_T", bound=ChopResult)
+
+
class ChopResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Choppiness Index (CHOP) results.
diff --git a/stock_indicators/indicators/cmf.py b/stock_indicators/indicators/cmf.py
index a9aa24b9..878a99fe 100644
--- a/stock_indicators/indicators/cmf.py
+++ b/stock_indicators/indicators/cmf.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_cmf(quotes: Iterable[Quote], lookback_periods: int = 20):
@@ -63,6 +63,8 @@ def cmf(self, value):
_T = TypeVar("_T", bound=CMFResult)
+
+
class CMFResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Chaikin Money Flow (CMF) results.
diff --git a/stock_indicators/indicators/cmo.py b/stock_indicators/indicators/cmo.py
index 0e6ed714..45fa6c16 100644
--- a/stock_indicators/indicators/cmo.py
+++ b/stock_indicators/indicators/cmo.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_cmo(quotes: Iterable[Quote], lookback_periods: int):
@@ -47,6 +47,8 @@ def cmo(self, value):
_T = TypeVar("_T", bound=CMOResult)
+
+
class CMOResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Chande Momentum Oscillator (CMO) results.
diff --git a/stock_indicators/indicators/common/__init__.py b/stock_indicators/indicators/common/__init__.py
index 06994367..d869d097 100644
--- a/stock_indicators/indicators/common/__init__.py
+++ b/stock_indicators/indicators/common/__init__.py
@@ -1,35 +1,30 @@
-from .quote import Quote
-from .results import (
- ResultBase,
- IndicatorResults
-)
-from .candles import (
- CandleProperties,
-)
+from .candles import CandleProperties
from .enums import (
BetaType,
- ChandelierType,
CandlePart,
+ ChandelierType,
EndType,
+ Match,
MAType,
PeriodSize,
PivotPointType,
PivotTrend,
- Match
)
+from .quote import Quote
+from .results import IndicatorResults, ResultBase
__all__ = [
- "Quote",
- "ResultBase",
- "IndicatorResults",
- "CandleProperties",
"BetaType",
- "ChandelierType",
"CandlePart",
+ "CandleProperties",
+ "ChandelierType",
"EndType",
+ "IndicatorResults",
"MAType",
+ "Match",
"PeriodSize",
"PivotPointType",
"PivotTrend",
- "Match"
+ "Quote",
+ "ResultBase",
]
diff --git a/stock_indicators/indicators/common/_contrib/type_resolver.py b/stock_indicators/indicators/common/_contrib/type_resolver.py
index 59876c61..6bdcfdf1 100644
--- a/stock_indicators/indicators/common/_contrib/type_resolver.py
+++ b/stock_indicators/indicators/common/_contrib/type_resolver.py
@@ -1,5 +1,16 @@
from typing import Type, TypeVar, cast
_T = TypeVar("_T")
-def generate_cs_inherited_class(child: Type[_T], cs_parent: Type, class_name="_Wrapper"):
- return cast(Type[_T], type(class_name, (cs_parent, ), {attr: getattr(child, attr) for attr in dir(child)}))
+
+
+def generate_cs_inherited_class(
+ child: Type[_T], cs_parent: Type, class_name="_Wrapper"
+):
+ return cast(
+ Type[_T],
+ type(
+ class_name,
+ (cs_parent,),
+ {attr: getattr(child, attr) for attr in dir(child)},
+ ),
+ )
diff --git a/stock_indicators/indicators/common/candles.py b/stock_indicators/indicators/common/candles.py
index a6345fab..d1d94559 100644
--- a/stock_indicators/indicators/common/candles.py
+++ b/stock_indicators/indicators/common/candles.py
@@ -1,11 +1,14 @@
from decimal import Decimal
from typing import Optional, TypeVar
+
from typing_extensions import override
from stock_indicators._cslib import CsCandleProperties
from stock_indicators._cstypes import Decimal as CsDecimal
from stock_indicators._cstypes import to_pydecimal
-from stock_indicators.indicators.common._contrib.type_resolver import generate_cs_inherited_class
+from stock_indicators.indicators.common._contrib.type_resolver import (
+ generate_cs_inherited_class,
+)
from stock_indicators.indicators.common.enums import Match
from stock_indicators.indicators.common.helpers import CondenseMixin
from stock_indicators.indicators.common.quote import _Quote
@@ -15,26 +18,31 @@
class _CandleProperties(_Quote):
@property
def size(self) -> Optional[Decimal]:
+ # pylint: disable=no-member # C# interop properties
return to_pydecimal(self.High - self.Low)
@property
def body(self) -> Optional[Decimal]:
- return to_pydecimal(self.Open - self.Close \
- if (self.Open > self.Close) \
- else self.Close - self.Open)
+ # pylint: disable=no-member # C# interop properties
+ return to_pydecimal(
+ self.Open - self.Close
+ if (self.Open > self.Close)
+ else self.Close - self.Open
+ )
@property
def upper_wick(self) -> Optional[Decimal]:
- return to_pydecimal(self.High - (
- self.Open \
- if self.Open > self.Close \
- else self.Close))
+ # pylint: disable=no-member # C# interop properties
+ return to_pydecimal(
+ self.High - (self.Open if self.Open > self.Close else self.Close)
+ )
@property
def lower_wick(self) -> Optional[Decimal]:
- return to_pydecimal((self.Close \
- if self.Open > self.Close \
- else self.Open) - self.Low)
+ # pylint: disable=no-member # C# interop properties
+ return to_pydecimal(
+ (self.Close if self.Open > self.Close else self.Open) - self.Low
+ )
@property
def body_pct(self) -> Optional[float]:
@@ -50,14 +58,18 @@ def lower_wick_pct(self) -> Optional[float]:
@property
def is_bullish(self) -> bool:
+ # pylint: disable=no-member # C# interop properties
return self.Close > self.Open
@property
def is_bearish(self) -> bool:
+ # pylint: disable=no-member # C# interop properties
return self.Close < self.Open
-class CandleProperties(generate_cs_inherited_class(_CandleProperties, CsCandleProperties)):
+class CandleProperties(
+ generate_cs_inherited_class(_CandleProperties, CsCandleProperties)
+):
"""An extended version of Quote that contains additional calculated properties."""
@@ -87,7 +99,10 @@ def match(self, value):
@property
def candle(self) -> CandleProperties:
if not self.__candle_prop_cache:
- self.__candle_prop_cache = CandleProperties.from_csquote(self._csdata.Candle)
+ # pylint: disable=no-member # C# interop method
+ self.__candle_prop_cache = CandleProperties.from_csquote(
+ self._csdata.Candle
+ )
return self.__candle_prop_cache
@@ -98,6 +113,8 @@ def candle(self, value):
_T = TypeVar("_T", bound=CandleResult)
+
+
class CandleResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Candlestick pattern results.
@@ -107,4 +124,6 @@ class CandleResults(CondenseMixin, IndicatorResults[_T]):
@override
def condense(self):
- return self.__class__(filter(lambda x: x.match != Match.NONE, self), self._wrapper_class)
+ return self.__class__(
+ filter(lambda x: x.match != Match.NONE, self), self._wrapper_class
+ )
diff --git a/stock_indicators/indicators/common/enums.py b/stock_indicators/indicators/common/enums.py
index 60a40c76..a9296e8f 100644
--- a/stock_indicators/indicators/common/enums.py
+++ b/stock_indicators/indicators/common/enums.py
@@ -1,6 +1,15 @@
from stock_indicators._cslib import (
- CsCandlePart, CsMatch, CsEnum, CsBetaType, CsChandelierType, CsMaType,
- CsPivotPointType, CsPeriodSize, CsEndType, CsPivotTrend)
+ CsBetaType,
+ CsCandlePart,
+ CsChandelierType,
+ CsEndType,
+ CsEnum,
+ CsMatch,
+ CsMaType,
+ CsPeriodSize,
+ CsPivotPointType,
+ CsPivotTrend,
+)
from stock_indicators.indicators.common._contrib.enum import CsCompatibleIntEnum
diff --git a/stock_indicators/indicators/common/helpers.py b/stock_indicators/indicators/common/helpers.py
index 12343b17..d019dc14 100644
--- a/stock_indicators/indicators/common/helpers.py
+++ b/stock_indicators/indicators/common/helpers.py
@@ -2,37 +2,69 @@
from typing_extensions import Self
-from stock_indicators._cslib import CsIndicator, CsIEnumerable, CsResultUtility
+from stock_indicators._cslib import CsIEnumerable, CsIndicator, CsResultUtility
from stock_indicators._cstypes import List as CsList
+from stock_indicators.exceptions import IndicatorCalculationError
from stock_indicators.indicators.common.results import IndicatorResults
class RemoveWarmupMixin:
"""IndicatorResults Mixin for remove_warmup_periods()."""
+
@IndicatorResults._verify_data
- def remove_warmup_periods(self: IndicatorResults, remove_periods: Optional[int] = None) -> Self:
+ def remove_warmup_periods(
+ self: IndicatorResults, remove_periods: Optional[int] = None
+ ) -> Self:
"""
Remove the recommended(or specified) quantity of results from the beginning of the results list.
+
+ Args:
+ remove_periods: Number of periods to remove. If None, removes recommended warmup periods.
+
+ Returns:
+ New IndicatorResults instance with warmup periods removed.
"""
if remove_periods is not None:
+ if not isinstance(remove_periods, int):
+ raise TypeError("remove_periods must be an integer")
+ if remove_periods < 0:
+ raise ValueError("remove_periods must be non-negative")
return super().remove_warmup_periods(remove_periods)
- removed_results = CsIndicator.RemoveWarmupPeriods(CsList(self._get_csdata_type(), self._csdata))
- return self.__class__(removed_results, self._wrapper_class)
+ try:
+ removed_results = CsIndicator.RemoveWarmupPeriods(
+ CsList(self._get_csdata_type(), self._csdata)
+ )
+ return self.__class__(removed_results, self._wrapper_class)
+ except Exception as e:
+ raise IndicatorCalculationError("remove_warmup_periods failed") from e
class CondenseMixin:
"""IndicatorResults Mixin for condense()."""
+
@IndicatorResults._verify_data
def condense(self: IndicatorResults) -> Self:
"""
Removes non-essential records containing null values with unique consideration for this indicator.
+
+ Returns:
+ New IndicatorResults instance with null values removed.
"""
cs_results_type = self._get_csdata_type()
- try: # to check whether there's matched overloaded method.
- condense_method = CsIndicator.Condense.Overloads[CsIEnumerable[cs_results_type]]
- except TypeError:
- condense_method = CsResultUtility.Condense[cs_results_type]
- condensed_results = condense_method(CsList(cs_results_type, self._csdata))
- return self.__class__(condensed_results, self._wrapper_class)
+ try:
+ # Try to find the specific overloaded method first
+ try:
+ condense_method = CsIndicator.Condense.Overloads[
+ CsIEnumerable[cs_results_type]
+ ]
+ except TypeError:
+ # Fall back to generic utility method
+ condense_method = CsResultUtility.Condense[cs_results_type]
+
+ condensed_results = condense_method(CsList(cs_results_type, self._csdata))
+ return self.__class__(condensed_results, self._wrapper_class)
+
+ except Exception as e:
+ raise ValueError("Failed to condense results") from e
diff --git a/stock_indicators/indicators/common/quote.py b/stock_indicators/indicators/common/quote.py
index dd986851..a3d29788 100644
--- a/stock_indicators/indicators/common/quote.py
+++ b/stock_indicators/indicators/common/quote.py
@@ -1,55 +1,94 @@
from datetime import datetime, timezone
from decimal import Decimal
-from typing import Any, Iterable, Optional
+from typing import Any, Iterable, Optional, Union
from stock_indicators._cslib import CsQuote, CsQuoteUtility
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import DateTime as CsDateTime
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydatetime, to_pydecimal
+from stock_indicators.indicators.common._contrib.type_resolver import (
+ generate_cs_inherited_class,
+)
from stock_indicators.indicators.common.enums import CandlePart
-from stock_indicators.indicators.common._contrib.type_resolver import generate_cs_inherited_class
-def _get_date(quote):
+def _get_date(quote) -> datetime:
+ """Get the date property with proper null handling."""
return to_pydatetime(quote.Date)
-def _set_date(quote, value):
+
+def _set_date(quote, value: datetime) -> None:
+ """Set the date property with validation and timezone normalization."""
+ if not isinstance(value, datetime):
+ raise TypeError("Date must be a datetime.datetime instance")
+
+ # Normalize timezone-aware datetime to UTC (from main branch)
if value.tzinfo is not None and value.utcoffset() is not None:
value = value.astimezone(timezone.utc)
quote.Date = CsDateTime(value)
-def _get_open(quote):
+
+def _get_open(quote) -> Optional[Decimal]:
+ """Get the open property with proper null handling."""
return to_pydecimal(quote.Open)
-def _set_open(quote, value):
- quote.Open = CsDecimal(value)
-def _get_high(quote):
+def _set_open(quote, value: Optional[Union[int, float, Decimal, str]]) -> None:
+ """Set the open property with validation."""
+ if value is not None:
+ quote.Open = CsDecimal(value)
+ # Note: C# nullable decimals can't be explicitly set to None from Python.NET
+ # The C# property handles null values internally when not set
+
+
+def _get_high(quote) -> Optional[Decimal]:
+ """Get the high property with proper null handling."""
return to_pydecimal(quote.High)
-def _set_high(quote, value):
- quote.High = CsDecimal(value)
-def _get_low(quote):
+def _set_high(quote, value: Optional[Union[int, float, Decimal, str]]) -> None:
+ """Set the high property with validation."""
+ if value is not None:
+ quote.High = CsDecimal(value)
+
+
+def _get_low(quote) -> Optional[Decimal]:
+ """Get the low property with proper null handling."""
return to_pydecimal(quote.Low)
-def _set_low(quote, value):
- quote.Low = CsDecimal(value)
-def _get_close(quote):
+def _set_low(quote, value: Optional[Union[int, float, Decimal, str]]) -> None:
+ """Set the low property with validation."""
+ if value is not None:
+ quote.Low = CsDecimal(value)
+
+
+def _get_close(quote) -> Optional[Decimal]:
+ """Get the close property with proper null handling."""
return to_pydecimal(quote.Close)
-def _set_close(quote, value):
- quote.Close = CsDecimal(value)
-def _get_volume(quote):
+def _set_close(quote, value: Optional[Union[int, float, Decimal, str]]) -> None:
+ """Set the close property with validation."""
+ if value is not None:
+ quote.Close = CsDecimal(value)
+
+
+def _get_volume(quote) -> Optional[Decimal]:
+ """Get the volume property with proper null handling."""
return to_pydecimal(quote.Volume)
-def _set_volume(quote, value):
- quote.Volume = CsDecimal(value)
+
+def _set_volume(quote, value: Optional[Union[int, float, Decimal, str]]) -> None:
+ """Set the volume property with validation."""
+ if value is not None:
+ quote.Volume = CsDecimal(value)
+
class _Quote:
+ """Internal Quote implementation with property definitions."""
+
date = property(_get_date, _set_date)
open = property(_get_open, _set_open)
high = property(_get_high, _set_high)
@@ -57,39 +96,87 @@ class _Quote:
close = property(_get_close, _set_close)
volume = property(_get_volume, _set_volume)
- def __init__(self, date: datetime, open: Optional[Any] = None,
- high: Optional[Any] = None, low: Optional[Any] = None,
- close: Optional[Any] = None, volume: Optional[Any] = None):
+ def __init__(
+ self,
+ date: datetime, # pylint: disable=too-many-positional-arguments
+ open: Optional[Union[int, float, Decimal, str]] = None, # pylint: disable=redefined-builtin
+ high: Optional[Union[int, float, Decimal, str]] = None,
+ low: Optional[Union[int, float, Decimal, str]] = None,
+ close: Optional[Union[int, float, Decimal, str]] = None,
+ volume: Optional[Union[int, float, Decimal, str]] = None,
+ ):
+ """
+ Initialize a Quote with OHLCV data.
+
+ Args:
+ date: The date for this quote (required)
+ open: Opening price (optional)
+ high: High price (optional)
+ low: Low price (optional)
+ close: Closing price (optional)
+ volume: Volume (optional)
+ """
+ if not isinstance(date, datetime):
+ raise TypeError("date must be a datetime.datetime instance")
+
self.date = date
- self.open: Decimal = open if open else 0
- self.high: Decimal = high if high else 0
- self.low: Decimal = low if low else 0
- self.close: Decimal = close if close else 0
- self.volume: Decimal = volume if volume else 0
+ # Only set values that are not None to avoid C# nullable issues
+ if open is not None:
+ self.open = open
+ if high is not None:
+ self.high = high
+ if low is not None:
+ self.low = low
+ if close is not None:
+ self.close = close
+ if volume is not None:
+ self.volume = volume
@classmethod
- def from_csquote(cls, cs_quote: CsQuote):
+ def from_csquote(cls, cs_quote: CsQuote) -> "Quote":
"""Constructs `Quote` instance from C# `Quote` instance."""
+ if not isinstance(cs_quote, CsQuote):
+ raise TypeError("cs_quote must be a C# Quote instance")
+
return cls(
date=to_pydatetime(cs_quote.Date),
open=to_pydecimal(cs_quote.Open),
high=to_pydecimal(cs_quote.High),
low=to_pydecimal(cs_quote.Low),
close=to_pydecimal(cs_quote.Close),
- volume=to_pydecimal(cs_quote.Volume)
+ volume=to_pydecimal(cs_quote.Volume),
)
@classmethod
- def use(cls, quotes: Iterable["Quote"], candle_part: CandlePart):
+ def use(cls, quotes: Iterable["Quote"], candle_part: CandlePart) -> Any:
"""
Optionally select which candle part to use in the calculation.
It returns C# Object.
+
+ Args:
+ quotes: Collection of Quote objects
+ candle_part: Which part of the candle to use
+
+ Returns:
+ C# collection prepared for indicator calculation
"""
- return CsQuoteUtility.Use[Quote](CsList(Quote, quotes), candle_part.cs_value)
+ if not hasattr(quotes, "__iter__"):
+ raise TypeError("quotes must be iterable")
+ if not isinstance(candle_part, CandlePart):
+ raise TypeError("candle_part must be a CandlePart enum value")
+
+ try:
+ return CsQuoteUtility.Use[Quote](
+ CsList(Quote, quotes), candle_part.cs_value
+ )
+ except Exception as e:
+ raise ValueError(f"Failed to prepare quotes for calculation: {e}") from e
class Quote(generate_cs_inherited_class(_Quote, CsQuote)):
"""
A single dated quote containing OHLCV elements.
OHLCV values can be given as any object that can be represented as a number string.
+
+ This class extends the C# Quote type to provide Python-friendly access to quote data.
"""
diff --git a/stock_indicators/indicators/common/results.py b/stock_indicators/indicators/common/results.py
index 2424283d..d0510990 100644
--- a/stock_indicators/indicators/common/results.py
+++ b/stock_indicators/indicators/common/results.py
@@ -1,6 +1,5 @@
from datetime import datetime as PyDateTime
from typing import Callable, Iterable, List, Optional, Type, TypeVar
-from warnings import warn
from stock_indicators._cslib import CsResultBase
from stock_indicators._cstypes import DateTime as CsDateTime
@@ -9,59 +8,63 @@
class ResultBase:
"""A base wrapper class for a single unit of the results."""
+
def __init__(self, base_result: CsResultBase):
self._csdata = base_result
@property
- def date(self):
+ def date(self) -> PyDateTime:
+ """Get the date of this result."""
return to_pydatetime(self._csdata.Date)
@date.setter
- def date(self, value):
+ def date(self, value: PyDateTime) -> None:
+ """Set the date of this result."""
+ if not isinstance(value, PyDateTime):
+ raise TypeError("Date must be a datetime.datetime instance")
self._csdata.Date = CsDateTime(value)
_T = TypeVar("_T", bound=ResultBase)
+
+
class IndicatorResults(List[_T]):
"""
A base wrapper class for the list of results.
It provides helper methods written in CSharp implementation.
"""
+
def __init__(self, data: Iterable, wrapper_class: Type[_T]):
- super().__init__(map(wrapper_class, data))
- self._csdata = data
+ if not data:
+ super().__init__()
+ self._csdata = []
+ else:
+ super().__init__(map(wrapper_class, data))
+ self._csdata = data
self._wrapper_class = wrapper_class
- def reload(self):
- """
- Reload a C# array of the results to perform more operations.
- It is usually called after `done()`.
- This method is deprecated. It will be removed in the next version.
- """
- warn('This method is deprecated.', DeprecationWarning, stacklevel=2)
- if self._csdata is None:
- self._csdata = [ _._csdata for _ in self ]
- return self
-
- def done(self):
- """
- Remove a C# array of the results after finishing all operations.
- It is not necessary but saves memory.
- This method is deprecated. It will be removed in the next version.
- """
- warn('This method is deprecated.', DeprecationWarning, stacklevel=2)
- self._csdata = None
- return self
-
def _get_csdata_type(self):
"""Get C# result object type."""
+ if len(self) == 0:
+ raise ValueError("Cannot determine C# data type from empty results")
return type(self[0]._csdata)
- def _verify_data(func: Callable):
+ @staticmethod # pylint: disable=no-self-argument
+ def _verify_data(func: Callable) -> Callable:
"""Check whether `_csdata` can be passed to helper method."""
+
def verify_data(self, *args):
+ if self._csdata is None:
+ # Use a generic name when func.__name__ is not available
+ func_name = getattr(func, "__name__", "method")
+ raise ValueError(
+ f"Cannot {func_name}() after done() has been called. Call reload() first."
+ )
+
if not isinstance(self._csdata, Iterable) or len(self) < 1:
- raise ValueError(f"Cannot {func.__name__}() an empty result.")
+ # Use a generic name when func.__name__ is not available
+ func_name = getattr(func, "__name__", "method")
+ raise ValueError(f"Cannot {func_name}() an empty result.")
if not issubclass(self._get_csdata_type(), CsResultBase):
raise TypeError(
@@ -74,23 +77,52 @@ def verify_data(self, *args):
@_verify_data
def __add__(self, other: "IndicatorResults"):
- return self.__class__(list(self._csdata).__add__(list(other._csdata)), self._wrapper_class)
+ """Concatenate two IndicatorResults."""
+ if not isinstance(other, IndicatorResults):
+ raise TypeError("Can only add IndicatorResults to IndicatorResults")
+ return self.__class__(
+ list(self._csdata).__add__(list(other._csdata)), self._wrapper_class
+ )
@_verify_data
def __mul__(self, value: int):
+ """Repeat IndicatorResults."""
+ if not isinstance(value, int):
+ raise TypeError("Can only multiply IndicatorResults by integer")
return self.__class__(list(self._csdata).__mul__(value), self._wrapper_class)
@_verify_data
- def remove_warmup_periods(self, remove_periods: int):
+ def remove_warmup_periods(self, remove_periods: int) -> "IndicatorResults":
"""Remove a specific quantity of results from the beginning of the results list."""
if not isinstance(remove_periods, int):
raise TypeError("remove_periods must be an integer.")
+ if remove_periods < 0:
+ raise ValueError("remove_periods must be non-negative.")
+
+ if remove_periods >= len(self):
+ return self.__class__([], self._wrapper_class)
+
return self.__class__(list(self._csdata)[remove_periods:], self._wrapper_class)
def find(self, lookup_date: PyDateTime) -> Optional[_T]:
- """Find indicator values on a specific date. It returns `None` if no result found."""
+ """
+ Find indicator values on a specific date.
+ Returns `None` if no result found.
+
+ Args:
+ lookup_date: The date to search for
+
+ Returns:
+ The result for the given date or None if not found
+ """
if not isinstance(lookup_date, PyDateTime):
raise TypeError("lookup_date must be an instance of datetime.datetime.")
- return next((r for r in self if r.date == lookup_date), None)
+ # Linear search (result sets are usually small enough that this is sufficient)
+ # First try matching only the calendar date (ignoring time) for convenience.
+ # If that attribute access fails, fall back to exact datetime comparison.
+ try:
+ return next((r for r in self if r.date.date() == lookup_date.date()), None)
+ except (AttributeError, TypeError):
+ return next((r for r in self if r.date == lookup_date), None)
diff --git a/stock_indicators/indicators/connors_rsi.py b/stock_indicators/indicators/connors_rsi.py
index 1e0af0ec..73295d46 100644
--- a/stock_indicators/indicators/connors_rsi.py
+++ b/stock_indicators/indicators/connors_rsi.py
@@ -3,12 +3,16 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_connors_rsi(quotes: Iterable[Quote], rsi_periods: int = 3,
- streak_periods: int = 2, rank_periods: int = 100):
+def get_connors_rsi(
+ quotes: Iterable[Quote],
+ rsi_periods: int = 3,
+ streak_periods: int = 2,
+ rank_periods: int = 100,
+):
"""Get Connors RSI calculated.
Connors RSI is a composite oscillator that incorporates
@@ -35,8 +39,9 @@ def get_connors_rsi(quotes: Iterable[Quote], rsi_periods: int = 3,
- [Connors RSI Reference](https://python.stockindicators.dev/indicators/ConnorsRsi/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetConnorsRsi[Quote](CsList(Quote, quotes), rsi_periods,
- streak_periods, rank_periods)
+ results = CsIndicator.GetConnorsRsi[Quote](
+ CsList(Quote, quotes), rsi_periods, streak_periods, rank_periods
+ )
return ConnorsRSIResults(results, ConnorsRSIResult)
@@ -81,6 +86,8 @@ def connors_rsi(self, value):
_T = TypeVar("_T", bound=ConnorsRSIResult)
+
+
class ConnorsRSIResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Connors RSI results.
diff --git a/stock_indicators/indicators/correlation.py b/stock_indicators/indicators/correlation.py
index 630d771e..6bdbbb1b 100644
--- a/stock_indicators/indicators/correlation.py
+++ b/stock_indicators/indicators/correlation.py
@@ -3,12 +3,13 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_correlation(quotes_a: Iterable[Quote], quotes_b: Iterable[Quote],
- lookback_periods: int):
+def get_correlation(
+ quotes_a: Iterable[Quote], quotes_b: Iterable[Quote], lookback_periods: int
+):
"""Get Correlation Coefficient calculated.
Correlation Coefficient between two quote histories, based on Close price.
@@ -31,8 +32,9 @@ def get_correlation(quotes_a: Iterable[Quote], quotes_b: Iterable[Quote],
- [Correlation Coefficient Reference](https://python.stockindicators.dev/indicators/Correlation/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetCorrelation[Quote](CsList(Quote, quotes_a), CsList(Quote, quotes_b),
- lookback_periods)
+ results = CsIndicator.GetCorrelation[Quote](
+ CsList(Quote, quotes_a), CsList(Quote, quotes_b), lookback_periods
+ )
return CorrelationResults(results, CorrelationResult)
@@ -83,6 +85,8 @@ def r_squared(self, value):
_T = TypeVar("_T", bound=CorrelationResult)
+
+
class CorrelationResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Correlation Coefficient results.
diff --git a/stock_indicators/indicators/dema.py b/stock_indicators/indicators/dema.py
index bc7b54af..64253d04 100644
--- a/stock_indicators/indicators/dema.py
+++ b/stock_indicators/indicators/dema.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_dema(quotes: Iterable[Quote], lookback_periods: int):
@@ -46,6 +46,8 @@ def dema(self, value):
_T = TypeVar("_T", bound=DEMAResult)
+
+
class DEMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Double Exponential Moving Average (DEMA) results.
diff --git a/stock_indicators/indicators/doji.py b/stock_indicators/indicators/doji.py
index 23874df7..aa567457 100644
--- a/stock_indicators/indicators/doji.py
+++ b/stock_indicators/indicators/doji.py
@@ -28,5 +28,7 @@ def get_doji(quotes: Iterable[Quote], max_price_change_percent: float = 0.1):
- [Doji Reference](https://python.stockindicators.dev/indicators/Doji/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetDoji[Quote](CsList(Quote, quotes), max_price_change_percent)
+ results = CsIndicator.GetDoji[Quote](
+ CsList(Quote, quotes), max_price_change_percent
+ )
return CandleResults(results, CandleResult)
diff --git a/stock_indicators/indicators/donchian.py b/stock_indicators/indicators/donchian.py
index f577e109..f033ac47 100644
--- a/stock_indicators/indicators/donchian.py
+++ b/stock_indicators/indicators/donchian.py
@@ -2,12 +2,12 @@
from typing import Iterable, Optional, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_donchian(quotes: Iterable[Quote], lookback_periods: int = 20):
@@ -73,6 +73,8 @@ def width(self, value):
_T = TypeVar("_T", bound=DonchianResult)
+
+
class DonchianResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Donchian Channels results.
diff --git a/stock_indicators/indicators/dpo.py b/stock_indicators/indicators/dpo.py
index 6d2d9123..18a96d35 100644
--- a/stock_indicators/indicators/dpo.py
+++ b/stock_indicators/indicators/dpo.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_dpo(quotes: Iterable[Quote], lookback_periods: int):
@@ -55,6 +55,8 @@ def dpo(self, value):
_T = TypeVar("_T", bound=DPOResult)
+
+
class DPOResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Detrended Price Oscillator (DPO) results.
diff --git a/stock_indicators/indicators/dynamic.py b/stock_indicators/indicators/dynamic.py
index e6a9565c..ae3279f8 100644
--- a/stock_indicators/indicators/dynamic.py
+++ b/stock_indicators/indicators/dynamic.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_dynamic(quotes: Iterable[Quote], lookback_periods: int, k_factor: float = 0.6):
@@ -30,7 +30,9 @@ def get_dynamic(quotes: Iterable[Quote], lookback_periods: int, k_factor: float
- [McGinley Dynamic Reference](https://python.stockindicators.dev/indicators/Dynamic/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetDynamic[Quote](CsList(Quote, quotes), lookback_periods, k_factor)
+ results = CsIndicator.GetDynamic[Quote](
+ CsList(Quote, quotes), lookback_periods, k_factor
+ )
return DynamicResults(results, DynamicResult)
@@ -49,6 +51,8 @@ def dynamic(self, value):
_T = TypeVar("_T", bound=DynamicResult)
+
+
class DynamicResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of McGinley Dynamic results.
diff --git a/stock_indicators/indicators/elder_ray.py b/stock_indicators/indicators/elder_ray.py
index 1ac3a97c..2eaa9967 100644
--- a/stock_indicators/indicators/elder_ray.py
+++ b/stock_indicators/indicators/elder_ray.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_elder_ray(quotes: Iterable[Quote], lookback_periods: int = 13):
@@ -62,6 +62,8 @@ def bear_power(self, value):
_T = TypeVar("_T", bound=ElderRayResult)
+
+
class ElderRayResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Elder-ray Index results.
diff --git a/stock_indicators/indicators/ema.py b/stock_indicators/indicators/ema.py
index 8de55047..1a097f8e 100644
--- a/stock_indicators/indicators/ema.py
+++ b/stock_indicators/indicators/ema.py
@@ -3,12 +3,15 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators.indicators.common.enums import CandlePart
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_ema(quotes: Iterable[Quote], lookback_periods: int,
- candle_part: CandlePart = CandlePart.CLOSE):
+def get_ema(
+ quotes: Iterable[Quote],
+ lookback_periods: int,
+ candle_part: CandlePart = CandlePart.CLOSE,
+):
"""Get EMA calculated.
Exponential Moving Average (EMA) of the Close price.
@@ -31,7 +34,7 @@ def get_ema(quotes: Iterable[Quote], lookback_periods: int,
- [EMA Reference](https://python.stockindicators.dev/indicators/Ema/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- quotes = Quote.use(quotes, candle_part) # Error occurs if not assigned to local var.
+ quotes = Quote.use(quotes, candle_part) # pylint: disable=no-member # Error occurs if not assigned to local var.
ema_list = CsIndicator.GetEma(quotes, lookback_periods)
return EMAResults(ema_list, EMAResult)
@@ -51,6 +54,8 @@ def ema(self, value):
_T = TypeVar("_T", bound=EMAResult)
+
+
class EMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of EMA(Exponential Moving Average) results.
diff --git a/stock_indicators/indicators/epma.py b/stock_indicators/indicators/epma.py
index 5616cb83..7bc0e2cd 100644
--- a/stock_indicators/indicators/epma.py
+++ b/stock_indicators/indicators/epma.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_epma(quotes: Iterable[Quote], lookback_periods: int):
@@ -48,6 +48,8 @@ def epma(self, value):
_T = TypeVar("_T", bound=EPMAResult)
+
+
class EPMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Endpoint Moving Average (EPMA) results.
diff --git a/stock_indicators/indicators/fcb.py b/stock_indicators/indicators/fcb.py
index a7c6615b..ed29240a 100644
--- a/stock_indicators/indicators/fcb.py
+++ b/stock_indicators/indicators/fcb.py
@@ -2,12 +2,12 @@
from typing import Iterable, Optional, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_fcb(quotes: Iterable[Quote], window_span: int = 2):
@@ -59,6 +59,8 @@ def lower_band(self, value):
_T = TypeVar("_T", bound=FCBResult)
+
+
class FCBResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Fractal Chaos Bands (FCB) results.
diff --git a/stock_indicators/indicators/fisher_transform.py b/stock_indicators/indicators/fisher_transform.py
index b208c29d..637b7ef5 100644
--- a/stock_indicators/indicators/fisher_transform.py
+++ b/stock_indicators/indicators/fisher_transform.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_fisher_transform(quotes: Iterable[Quote], lookback_periods: int = 10):
@@ -29,7 +29,9 @@ def get_fisher_transform(quotes: Iterable[Quote], lookback_periods: int = 10):
- [Fisher Transform Reference](https://python.stockindicators.dev/indicators/FisherTransform/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetFisherTransform[Quote](CsList(Quote, quotes), lookback_periods)
+ results = CsIndicator.GetFisherTransform[Quote](
+ CsList(Quote, quotes), lookback_periods
+ )
return FisherTransformResults(results, FisherTransformResult)
@@ -56,6 +58,8 @@ def trigger(self, value):
_T = TypeVar("_T", bound=FisherTransformResult)
+
+
class FisherTransformResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Ehlers Fisher Transform results.
diff --git a/stock_indicators/indicators/force_index.py b/stock_indicators/indicators/force_index.py
index ec7fe444..e125dab9 100644
--- a/stock_indicators/indicators/force_index.py
+++ b/stock_indicators/indicators/force_index.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_force_index(quotes: Iterable[Quote], lookback_periods: int):
@@ -46,6 +46,8 @@ def force_index(self, value):
_T = TypeVar("_T", bound=ForceIndexResult)
+
+
class ForceIndexResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Force Index results.
diff --git a/stock_indicators/indicators/fractal.py b/stock_indicators/indicators/fractal.py
index 18548d63..7c676a58 100644
--- a/stock_indicators/indicators/fractal.py
+++ b/stock_indicators/indicators/fractal.py
@@ -2,20 +2,30 @@
from typing import Iterable, Optional, TypeVar, overload
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.enums import EndType
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
@overload
-def get_fractal(quotes: Iterable[Quote], window_span: int = 2, end_type = EndType.HIGH_LOW) -> "FractalResults[FractalResult]": ...
+def get_fractal(
+ quotes: Iterable[Quote], window_span: int = 2, end_type=EndType.HIGH_LOW
+) -> "FractalResults[FractalResult]": ...
+
+
@overload
-def get_fractal(quotes: Iterable[Quote], left_span: int, right_span: int, end_type = EndType.HIGH_LOW) -> "FractalResults[FractalResult]": ...
-def get_fractal(quotes, left_span = None, right_span = EndType.HIGH_LOW, end_type = EndType.HIGH_LOW):
+def get_fractal(
+ quotes: Iterable[Quote], left_span: int, right_span: int, end_type=EndType.HIGH_LOW
+) -> "FractalResults[FractalResult]": ...
+
+
+def get_fractal(
+ quotes, left_span=None, right_span=EndType.HIGH_LOW, end_type=EndType.HIGH_LOW
+):
"""Get Williams Fractal calculated.
Williams Fractal is a retrospective price pattern that
@@ -46,10 +56,15 @@ def get_fractal(quotes, left_span = None, right_span = EndType.HIGH_LOW, end_typ
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
if isinstance(right_span, EndType):
- if left_span is None: left_span = 2
- fractal_results = CsIndicator.GetFractal[Quote](CsList(Quote, quotes), left_span, right_span.cs_value)
+ if left_span is None:
+ left_span = 2
+ fractal_results = CsIndicator.GetFractal[Quote](
+ CsList(Quote, quotes), left_span, right_span.cs_value
+ )
else:
- fractal_results = CsIndicator.GetFractal[Quote](CsList(Quote, quotes), left_span, right_span, end_type.cs_value)
+ fractal_results = CsIndicator.GetFractal[Quote](
+ CsList(Quote, quotes), left_span, right_span, end_type.cs_value
+ )
return FractalResults(fractal_results, FractalResult)
@@ -77,6 +92,8 @@ def fractal_bull(self, value):
_T = TypeVar("_T", bound=FractalResult)
+
+
class FractalResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Williams Fractal results.
diff --git a/stock_indicators/indicators/gator.py b/stock_indicators/indicators/gator.py
index 885dcc98..a573bd96 100644
--- a/stock_indicators/indicators/gator.py
+++ b/stock_indicators/indicators/gator.py
@@ -4,8 +4,9 @@
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.alligator import AlligatorResult
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
+
@overload
def get_gator(quotes: Iterable[Quote]) -> "GatorResults[GatorResult]": ...
@@ -32,12 +33,12 @@ def get_gator(quotes):
results = CsIndicator.GetGator[Quote](CsList(Quote, quotes))
else:
# Get C# objects.
- if isinstance(quotes, IndicatorResults) and quotes._csdata is not None:
- cs_results = quotes._csdata
+ if isinstance(quotes, IndicatorResults):
+ # Use the C# data directly if available
+ results = CsIndicator.GetGator(quotes._csdata)
else:
- cs_results = [ q._csdata for q in quotes ]
-
- results = CsIndicator.GetGator(CsList(type(cs_results[0]), cs_results))
+ cs_results = [q._csdata for q in quotes]
+ results = CsIndicator.GetGator(CsList(type(cs_results[0]), cs_results))
return GatorResults(results, GatorResult)
@@ -80,6 +81,8 @@ def is_lower_expanding(self, value):
_T = TypeVar("_T", bound=GatorResult)
+
+
class GatorResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Gator Oscillator results.
diff --git a/stock_indicators/indicators/heikin_ashi.py b/stock_indicators/indicators/heikin_ashi.py
index 02674d0c..fd56345b 100644
--- a/stock_indicators/indicators/heikin_ashi.py
+++ b/stock_indicators/indicators/heikin_ashi.py
@@ -2,11 +2,11 @@
from typing import Iterable, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_heikin_ashi(quotes: Iterable[Quote]):
@@ -77,6 +77,8 @@ def volume(self, value):
_T = TypeVar("_T", bound=HeikinAshiResult)
+
+
class HeikinAshiResults(IndicatorResults[_T]):
"""
A wrapper class for the list of Heikin-Ashi results.
diff --git a/stock_indicators/indicators/hma.py b/stock_indicators/indicators/hma.py
index 7738ea26..9926d3f0 100644
--- a/stock_indicators/indicators/hma.py
+++ b/stock_indicators/indicators/hma.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_hma(quotes: Iterable[Quote], lookback_periods: int):
@@ -47,6 +47,8 @@ def hma(self, value):
_T = TypeVar("_T", bound=HMAResult)
+
+
class HMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Hull Moving Average (HMA) results.
diff --git a/stock_indicators/indicators/ht_trendline.py b/stock_indicators/indicators/ht_trendline.py
index 79843bb5..75a7b5fd 100644
--- a/stock_indicators/indicators/ht_trendline.py
+++ b/stock_indicators/indicators/ht_trendline.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_ht_trendline(quotes: Iterable[Quote]):
@@ -60,6 +60,8 @@ def smooth_price(self, value):
_T = TypeVar("_T", bound=HTTrendlineResult)
+
+
class HTTrendlineResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Hilbert Transform Instantaneous Trendline (HTL) results.
diff --git a/stock_indicators/indicators/hurst.py b/stock_indicators/indicators/hurst.py
index cea24f49..3b0f3c72 100644
--- a/stock_indicators/indicators/hurst.py
+++ b/stock_indicators/indicators/hurst.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_hurst(quotes: Iterable[Quote], lookback_periods: int = 100):
@@ -47,6 +47,8 @@ def hurst_exponent(self, value):
_T = TypeVar("_T", bound=HurstResult)
+
+
class HurstResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Hurst Exponent results.
diff --git a/stock_indicators/indicators/ichimoku.py b/stock_indicators/indicators/ichimoku.py
index 5a11444d..dd5e7f26 100644
--- a/stock_indicators/indicators/ichimoku.py
+++ b/stock_indicators/indicators/ichimoku.py
@@ -2,28 +2,56 @@
from typing import Iterable, Optional, TypeVar, overload
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
@overload
-def get_ichimoku(quotes: Iterable[Quote], tenkan_periods: int = 9,
- kijun_periods: int = 26, senkou_b_periods: int = 52) -> "IchimokuResults[IchimokuResult]": ...
+def get_ichimoku(
+ quotes: Iterable[Quote],
+ tenkan_periods: int = 9,
+ kijun_periods: int = 26,
+ senkou_b_periods: int = 52,
+) -> "IchimokuResults[IchimokuResult]": ...
+
+
@overload
-def get_ichimoku(quotes: Iterable[Quote], tenkan_periods: int,
- kijun_periods: int, senkou_b_periods: int,
- offset_periods: int) -> "IchimokuResults[IchimokuResult]": ...
+def get_ichimoku(
+ quotes: Iterable[Quote],
+ tenkan_periods: int,
+ kijun_periods: int,
+ senkou_b_periods: int,
+ *,
+ offset_periods: int,
+) -> "IchimokuResults[IchimokuResult]": ...
+
+
@overload
-def get_ichimoku(quotes: Iterable[Quote], tenkan_periods: int,
- kijun_periods: int, senkou_b_periods: int,
- senkou_offset: int, chikou_offset: int) -> "IchimokuResults[IchimokuResult]": ...
-def get_ichimoku(quotes: Iterable[Quote], tenkan_periods: int = None,
- kijun_periods: int = None, senkou_b_periods: int = None,
- senkou_offset: int = None, chikou_offset: int = None):
+def get_ichimoku(
+ quotes: Iterable[Quote],
+ tenkan_periods: int,
+ kijun_periods: int,
+ senkou_b_periods: int,
+ *,
+ senkou_offset: int,
+ chikou_offset: int,
+) -> "IchimokuResults[IchimokuResult]": ...
+
+
+def get_ichimoku(
+ quotes: Iterable[Quote],
+ tenkan_periods: int = 9,
+ kijun_periods: int = 26,
+ senkou_b_periods: int = 52,
+ senkou_offset: Optional[int] = None,
+ chikou_offset: Optional[int] = None,
+ *,
+ offset_periods: Optional[int] = None,
+) -> "IchimokuResults[IchimokuResult]": # pylint: disable=too-many-positional-arguments
"""Get Ichimoku Cloud calculated.
Ichimoku Cloud, also known as Ichimoku Kinkō Hyō, is a collection of indicators
@@ -60,17 +88,27 @@ def get_ichimoku(quotes: Iterable[Quote], tenkan_periods: int = None,
- [Ichimoku Cloud Reference](https://python.stockindicators.dev/indicators/Ichimoku/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
+ # Normalize offset_periods into senkou_offset and chikou_offset
+ if offset_periods is not None:
+ if senkou_offset is None:
+ senkou_offset = offset_periods
+ if chikou_offset is None:
+ chikou_offset = offset_periods
+
+ # Apply default logic when offsets are still None
if chikou_offset is None:
if senkou_offset is None:
- if tenkan_periods is None: tenkan_periods = 9
- if kijun_periods is None: kijun_periods = 26
- if senkou_b_periods is None: senkou_b_periods = 52
senkou_offset = kijun_periods
chikou_offset = senkou_offset
- results = CsIndicator.GetIchimoku[Quote](CsList(Quote, quotes), tenkan_periods,
- kijun_periods, senkou_b_periods,
- senkou_offset, chikou_offset)
+ results = CsIndicator.GetIchimoku[Quote](
+ CsList(Quote, quotes),
+ tenkan_periods,
+ kijun_periods,
+ senkou_b_periods,
+ senkou_offset,
+ chikou_offset,
+ )
return IchimokuResults(results, IchimokuResult)
@@ -121,6 +159,8 @@ def chikou_span(self, value):
_T = TypeVar("_T", bound=IchimokuResult)
+
+
class IchimokuResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Ichimoku Cloud results.
diff --git a/stock_indicators/indicators/kama.py b/stock_indicators/indicators/kama.py
index 8fefb9fc..720e3fea 100644
--- a/stock_indicators/indicators/kama.py
+++ b/stock_indicators/indicators/kama.py
@@ -3,15 +3,19 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_kama(quotes: Iterable[Quote], er_periods: int = 10,
- fast_periods: int = 2, slow_periods: int = 30):
+def get_kama(
+ quotes: Iterable[Quote],
+ er_periods: int = 10,
+ fast_periods: int = 2,
+ slow_periods: int = 30,
+):
"""Get KAMA calculated.
- Kaufman’s Adaptive Moving Average (KAMA) is an volatility
+ Kaufman's Adaptive Moving Average (KAMA) is an volatility
adaptive moving average of Close price over configurable lookback periods.
Parameters:
@@ -35,14 +39,15 @@ def get_kama(quotes: Iterable[Quote], er_periods: int = 10,
- [KAMA Reference](https://python.stockindicators.dev/indicators/Kama/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetKama[Quote](CsList(Quote, quotes), er_periods,
- fast_periods, slow_periods)
+ results = CsIndicator.GetKama[Quote](
+ CsList(Quote, quotes), er_periods, fast_periods, slow_periods
+ )
return KAMAResults(results, KAMAResult)
class KAMAResult(ResultBase):
"""
- A wrapper class for a single unit of Kaufman’s Adaptive Moving Average (KAMA) results.
+ A wrapper class for a single unit of Kaufman's Adaptive Moving Average (KAMA) results.
"""
@property
@@ -63,9 +68,11 @@ def kama(self, value):
_T = TypeVar("_T", bound=KAMAResult)
+
+
class KAMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
- A wrapper class for the list of Kaufman’s Adaptive Moving Average (KAMA) results.
+ A wrapper class for the list of Kaufman's Adaptive Moving Average (KAMA) results.
It is exactly same with built-in `list` except for that it provides
some useful helper methods written in CSharp implementation.
"""
diff --git a/stock_indicators/indicators/keltner.py b/stock_indicators/indicators/keltner.py
index 57b79399..94920fbd 100644
--- a/stock_indicators/indicators/keltner.py
+++ b/stock_indicators/indicators/keltner.py
@@ -3,12 +3,16 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_keltner(quotes: Iterable[Quote], ema_periods: int = 20,
- multiplier: float = 2, atr_periods: int = 10):
+def get_keltner(
+ quotes: Iterable[Quote],
+ ema_periods: int = 20,
+ multiplier: float = 2,
+ atr_periods: int = 10,
+):
"""Get Keltner Channels calculated.
Keltner Channels are based on an EMA centerline andATR band widths.
@@ -35,8 +39,9 @@ def get_keltner(quotes: Iterable[Quote], ema_periods: int = 20,
- [Keltner Channels Reference](https://python.stockindicators.dev/indicators/Keltner/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetKeltner[Quote](CsList(Quote, quotes), ema_periods,
- multiplier, atr_periods)
+ results = CsIndicator.GetKeltner[Quote](
+ CsList(Quote, quotes), ema_periods, multiplier, atr_periods
+ )
return KeltnerResults(results, KeltnerResult)
@@ -79,6 +84,8 @@ def width(self, value):
_T = TypeVar("_T", bound=KeltnerResult)
+
+
class KeltnerResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Keltner Channels results.
diff --git a/stock_indicators/indicators/kvo.py b/stock_indicators/indicators/kvo.py
index cea328dc..9bcb2d8f 100644
--- a/stock_indicators/indicators/kvo.py
+++ b/stock_indicators/indicators/kvo.py
@@ -3,12 +3,16 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_kvo(quotes: Iterable[Quote], fast_periods: int = 34,
- slow_periods: int = 55, signal_periods: int = 13):
+def get_kvo(
+ quotes: Iterable[Quote],
+ fast_periods: int = 34,
+ slow_periods: int = 55,
+ signal_periods: int = 13,
+):
"""Get KVO calculated.
Klinger Volume Oscillator (KVO) depicts volume-based divergence
@@ -35,8 +39,9 @@ def get_kvo(quotes: Iterable[Quote], fast_periods: int = 34,
- [KVO Reference](https://python.stockindicators.dev/indicators/Kvo/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetKvo[Quote](CsList(Quote, quotes), fast_periods,
- slow_periods, signal_periods)
+ results = CsIndicator.GetKvo[Quote](
+ CsList(Quote, quotes), fast_periods, slow_periods, signal_periods
+ )
return KVOResults(results, KVOResult)
@@ -63,6 +68,8 @@ def signal(self, value):
_T = TypeVar("_T", bound=KVOResult)
+
+
class KVOResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Klinger Volume Oscillator (KVO) results.
diff --git a/stock_indicators/indicators/ma_envelopes.py b/stock_indicators/indicators/ma_envelopes.py
index 55ccccd7..5be93337 100644
--- a/stock_indicators/indicators/ma_envelopes.py
+++ b/stock_indicators/indicators/ma_envelopes.py
@@ -4,12 +4,16 @@
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.enums import MAType
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_ma_envelopes(quotes: Iterable[Quote], lookback_periods: int,
- percent_offset: float = 2.5, ma_type: MAType = MAType.SMA):
+def get_ma_envelopes(
+ quotes: Iterable[Quote],
+ lookback_periods: int,
+ percent_offset: float = 2.5,
+ ma_type: MAType = MAType.SMA,
+):
"""Get Moving Average Envelopes calculated.
Moving Average Envelopes is a price band overlay that is offset
@@ -36,8 +40,9 @@ def get_ma_envelopes(quotes: Iterable[Quote], lookback_periods: int,
- [Moving Average Envelopes Reference](https://python.stockindicators.dev/indicators/MaEnvelopes/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetMaEnvelopes[Quote](CsList(Quote, quotes), lookback_periods,
- percent_offset, ma_type.cs_value)
+ results = CsIndicator.GetMaEnvelopes[Quote](
+ CsList(Quote, quotes), lookback_periods, percent_offset, ma_type.cs_value
+ )
return MAEnvelopeResults(results, MAEnvelopeResult)
@@ -72,6 +77,8 @@ def lower_envelope(self, value):
_T = TypeVar("_T", bound=MAEnvelopeResult)
+
+
class MAEnvelopeResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Moving Average Envelopes results.
diff --git a/stock_indicators/indicators/macd.py b/stock_indicators/indicators/macd.py
index 647537b6..df3574dd 100644
--- a/stock_indicators/indicators/macd.py
+++ b/stock_indicators/indicators/macd.py
@@ -3,13 +3,17 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators.indicators.common.enums import CandlePart
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_macd(quotes: Iterable[Quote], fast_periods: int = 12,
- slow_periods: int = 26, signal_periods: int = 9,
- candle_part: CandlePart = CandlePart.CLOSE):
+def get_macd(
+ quotes: Iterable[Quote],
+ fast_periods: int = 12,
+ slow_periods: int = 26,
+ signal_periods: int = 9,
+ candle_part: CandlePart = CandlePart.CLOSE,
+):
"""Get MACD calculated.
Moving Average Convergence/Divergence (MACD) is a simple oscillator view
@@ -39,9 +43,11 @@ def get_macd(quotes: Iterable[Quote], fast_periods: int = 12,
- [MACD Reference](https://python.stockindicators.dev/indicators/Macd/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- quotes = Quote.use(quotes, candle_part) # Error occurs if not assigned to local var.
- macd_results = CsIndicator.GetMacd(quotes, fast_periods,
- slow_periods, signal_periods)
+ # pylint: disable=no-member # Error occurs if not assigned to local var.
+ quotes = Quote.use(quotes, candle_part)
+ macd_results = CsIndicator.GetMacd(
+ quotes, fast_periods, slow_periods, signal_periods
+ )
return MACDResults(macd_results, MACDResult)
@@ -92,7 +98,9 @@ def slow_ema(self, value):
_T = TypeVar("_T", bound=MACDResult)
-class MACDResults(CondenseMixin, RemoveWarmupMixin ,IndicatorResults[_T]):
+
+
+class MACDResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of MACD(Moving Average Convergence/Divergence) results.
It is exactly same with built-in `list` except for that it provides
diff --git a/stock_indicators/indicators/mama.py b/stock_indicators/indicators/mama.py
index f48e7cde..369041d3 100644
--- a/stock_indicators/indicators/mama.py
+++ b/stock_indicators/indicators/mama.py
@@ -3,12 +3,13 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_mama(quotes: Iterable[Quote], fast_limit: float = 0.5,
- slow_limit: float = 0.05):
+def get_mama(
+ quotes: Iterable[Quote], fast_limit: float = 0.5, slow_limit: float = 0.05
+):
"""Get MAMA calculated.
MESA Adaptive Moving Average (MAMA) is a 5-period
@@ -32,8 +33,7 @@ def get_mama(quotes: Iterable[Quote], fast_limit: float = 0.5,
- [MAMA Reference](https://python.stockindicators.dev/indicators/Mama/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetMama[Quote](CsList(Quote, quotes), fast_limit,
- slow_limit)
+ results = CsIndicator.GetMama[Quote](CsList(Quote, quotes), fast_limit, slow_limit)
return MAMAResults(results, MAMAResult)
@@ -60,6 +60,8 @@ def fama(self, value):
_T = TypeVar("_T", bound=MAMAResult)
+
+
class MAMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of MESA Adaptive Moving Average (MAMA) results.
diff --git a/stock_indicators/indicators/mfi.py b/stock_indicators/indicators/mfi.py
index f610c4d2..90edc546 100644
--- a/stock_indicators/indicators/mfi.py
+++ b/stock_indicators/indicators/mfi.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_mfi(quotes: Iterable[Quote], lookback_periods: int = 14):
@@ -47,6 +47,8 @@ def mfi(self, value):
_T = TypeVar("_T", bound=MFIResult)
+
+
class MFIResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Money Flow Index (MFI) results.
diff --git a/stock_indicators/indicators/obv.py b/stock_indicators/indicators/obv.py
index 0d3754cc..88ed0041 100644
--- a/stock_indicators/indicators/obv.py
+++ b/stock_indicators/indicators/obv.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_obv(quotes: Iterable[Quote], sma_periods: Optional[int] = None):
@@ -55,6 +55,8 @@ def obv_sma(self, value):
_T = TypeVar("_T", bound=OBVResult)
+
+
class OBVResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of On-balance Volume (OBV) results.
diff --git a/stock_indicators/indicators/parabolic_sar.py b/stock_indicators/indicators/parabolic_sar.py
index b3bd3b79..2728be25 100644
--- a/stock_indicators/indicators/parabolic_sar.py
+++ b/stock_indicators/indicators/parabolic_sar.py
@@ -3,18 +3,26 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
@overload
-def get_parabolic_sar(quotes: Iterable[Quote], acceleration_step: float = 0.02,
- max_acceleration_factor: float = 0.2) -> "ParabolicSARResults[ParabolicSARResult]": ...
+def get_parabolic_sar(
+ quotes: Iterable[Quote],
+ acceleration_step: float = 0.02,
+ max_acceleration_factor: float = 0.2,
+) -> "ParabolicSARResults[ParabolicSARResult]": ...
@overload
-def get_parabolic_sar(quotes: Iterable[Quote], acceleration_step: float,
- max_acceleration_factor: float, initial_factor: float) -> "ParabolicSARResults[ParabolicSARResult]": ...
-def get_parabolic_sar(quotes, acceleration_step = None,
- max_acceleration_factor = None, initial_factor = None):
+def get_parabolic_sar(
+ quotes: Iterable[Quote],
+ acceleration_step: float,
+ max_acceleration_factor: float,
+ initial_factor: float,
+) -> "ParabolicSARResults[ParabolicSARResult]": ...
+def get_parabolic_sar(
+ quotes, acceleration_step=None, max_acceleration_factor=None, initial_factor=None
+):
"""Get Parabolic SAR calculated.
Parabolic SAR (stop and reverse) is a price-time based indicator
@@ -42,13 +50,20 @@ def get_parabolic_sar(quotes, acceleration_step = None,
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
if initial_factor is None:
- if acceleration_step is None: acceleration_step = 0.02
- if max_acceleration_factor is None: max_acceleration_factor = 0.2
- results = CsIndicator.GetParabolicSar[Quote](CsList(Quote, quotes), acceleration_step,
- max_acceleration_factor)
+ if acceleration_step is None:
+ acceleration_step = 0.02
+ if max_acceleration_factor is None:
+ max_acceleration_factor = 0.2
+ results = CsIndicator.GetParabolicSar[Quote](
+ CsList(Quote, quotes), acceleration_step, max_acceleration_factor
+ )
else:
- results = CsIndicator.GetParabolicSar[Quote](CsList(Quote, quotes), acceleration_step,
- max_acceleration_factor, initial_factor)
+ results = CsIndicator.GetParabolicSar[Quote](
+ CsList(Quote, quotes),
+ acceleration_step,
+ max_acceleration_factor,
+ initial_factor,
+ )
return ParabolicSARResults(results, ParabolicSARResult)
@@ -76,6 +91,8 @@ def is_reversal(self, value):
_T = TypeVar("_T", bound=ParabolicSARResult)
+
+
class ParabolicSARResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Parabolic SAR(stop and reverse) results.
diff --git a/stock_indicators/indicators/pivot_points.py b/stock_indicators/indicators/pivot_points.py
index e7379b24..5537a182 100644
--- a/stock_indicators/indicators/pivot_points.py
+++ b/stock_indicators/indicators/pivot_points.py
@@ -2,17 +2,20 @@
from typing import Optional, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.enums import PeriodSize, PivotPointType
from stock_indicators.indicators.common.helpers import RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_pivot_points(quotes, window_size: PeriodSize,
- point_type: PivotPointType = PivotPointType.STANDARD):
+def get_pivot_points(
+ quotes,
+ window_size: PeriodSize,
+ point_type: PivotPointType = PivotPointType.STANDARD,
+):
"""Get Pivot Points calculated.
Pivot Points depict support and resistance levels, based on
@@ -37,8 +40,9 @@ def get_pivot_points(quotes, window_size: PeriodSize,
- [Pivot Points Reference](https://python.stockindicators.dev/indicators/PivotPoints/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetPivotPoints[Quote](CsList(Quote, quotes), window_size.cs_value,
- point_type.cs_value)
+ results = CsIndicator.GetPivotPoints[Quote](
+ CsList(Quote, quotes), window_size.cs_value, point_type.cs_value
+ )
return PivotPointsResults(results, PivotPointsResult)
@@ -121,6 +125,8 @@ def s4(self, value):
_T = TypeVar("_T", bound=PivotPointsResult)
+
+
class PivotPointsResults(RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Pivot Points results.
diff --git a/stock_indicators/indicators/pivots.py b/stock_indicators/indicators/pivots.py
index 26540ea9..afa55af4 100644
--- a/stock_indicators/indicators/pivots.py
+++ b/stock_indicators/indicators/pivots.py
@@ -2,18 +2,22 @@
from typing import Iterable, Optional, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.enums import EndType, PivotTrend
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_pivots(quotes: Iterable[Quote], left_span: int = 2,
- right_span: int = 2, max_trend_periods: int = 20,
- end_type: EndType = EndType.HIGH_LOW):
+def get_pivots(
+ quotes: Iterable[Quote],
+ left_span: int = 2,
+ right_span: int = 2,
+ max_trend_periods: int = 20,
+ end_type: EndType = EndType.HIGH_LOW,
+):
"""Get Pivots calculated.
Pivots is an extended version of Williams Fractal that includes
@@ -44,9 +48,13 @@ def get_pivots(quotes: Iterable[Quote], left_span: int = 2,
- [Pivots Reference](https://python.stockindicators.dev/indicators/Pivots/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetPivots[Quote](CsList(Quote, quotes), left_span,
- right_span, max_trend_periods,
- end_type.cs_value)
+ results = CsIndicator.GetPivots[Quote](
+ CsList(Quote, quotes),
+ left_span,
+ right_span,
+ max_trend_periods,
+ end_type.cs_value,
+ )
return PivotsResults(results, PivotsResult)
@@ -109,6 +117,8 @@ def low_trend(self, value):
_T = TypeVar("_T", bound=PivotsResult)
+
+
class PivotsResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Pivots results.
diff --git a/stock_indicators/indicators/pmo.py b/stock_indicators/indicators/pmo.py
index 1e42dbd9..5af2697a 100644
--- a/stock_indicators/indicators/pmo.py
+++ b/stock_indicators/indicators/pmo.py
@@ -3,12 +3,16 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_pmo(quotes: Iterable[Quote], time_periods: int = 35,
- smooth_periods: int = 20, signal_periods: int = 10):
+def get_pmo(
+ quotes: Iterable[Quote],
+ time_periods: int = 35,
+ smooth_periods: int = 20,
+ signal_periods: int = 10,
+):
"""Get PMO calculated.
Price Momentum Oscillator (PMO) is double-smoothed ROC
@@ -35,8 +39,9 @@ def get_pmo(quotes: Iterable[Quote], time_periods: int = 35,
- [PMO Reference](https://python.stockindicators.dev/indicators/Pmo/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetPmo[Quote](CsList(Quote, quotes), time_periods,
- smooth_periods, signal_periods)
+ results = CsIndicator.GetPmo[Quote](
+ CsList(Quote, quotes), time_periods, smooth_periods, signal_periods
+ )
return PMOResults(results, PMOResult)
@@ -63,6 +68,8 @@ def signal(self, value):
_T = TypeVar("_T", bound=PMOResult)
+
+
class PMOResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Price Momentum Oscillator (PMO) results.
diff --git a/stock_indicators/indicators/prs.py b/stock_indicators/indicators/prs.py
index 9eba1070..f54ce2b1 100644
--- a/stock_indicators/indicators/prs.py
+++ b/stock_indicators/indicators/prs.py
@@ -3,12 +3,16 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_prs(eval_quotes: Iterable[Quote], base_quotes: Iterable[Quote],
- lookback_periods: Optional[int] = None, sma_periods: Optional[int] = None):
+def get_prs(
+ eval_quotes: Iterable[Quote],
+ base_quotes: Iterable[Quote],
+ lookback_periods: Optional[int] = None,
+ sma_periods: Optional[int] = None,
+):
"""Get PRS calculated.
Price Relative Strength (PRS), also called Comparative Relative Strength,
@@ -40,8 +44,12 @@ def get_prs(eval_quotes: Iterable[Quote], base_quotes: Iterable[Quote],
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetPrs[Quote](CsList(Quote, eval_quotes), CsList(Quote, base_quotes),
- lookback_periods, sma_periods)
+ results = CsIndicator.GetPrs[Quote](
+ CsList(Quote, eval_quotes),
+ CsList(Quote, base_quotes),
+ lookback_periods,
+ sma_periods,
+ )
return PRSResults(results, PRSResult)
diff --git a/stock_indicators/indicators/pvo.py b/stock_indicators/indicators/pvo.py
index f1eba34f..46b33001 100644
--- a/stock_indicators/indicators/pvo.py
+++ b/stock_indicators/indicators/pvo.py
@@ -3,12 +3,16 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_pvo(quotes: Iterable[Quote], fast_periods: int = 12,
- slow_periods: int = 26, signal_periods: int = 9):
+def get_pvo(
+ quotes: Iterable[Quote],
+ fast_periods: int = 12,
+ slow_periods: int = 26,
+ signal_periods: int = 9,
+):
"""Get PVO calculated.
Percentage Volume Oscillator (PVO) is a simple oscillator view
@@ -35,8 +39,9 @@ def get_pvo(quotes: Iterable[Quote], fast_periods: int = 12,
- [PVO Reference](https://python.stockindicators.dev/indicators/Pvo/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetPvo[Quote](CsList(Quote, quotes), fast_periods,
- slow_periods, signal_periods)
+ results = CsIndicator.GetPvo[Quote](
+ CsList(Quote, quotes), fast_periods, slow_periods, signal_periods
+ )
return PVOResults(results, PVOResult)
@@ -71,6 +76,8 @@ def histogram(self, value):
_T = TypeVar("_T", bound=PVOResult)
+
+
class PVOResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Percentage Volume Oscillator (PVO) results.
diff --git a/stock_indicators/indicators/renko.py b/stock_indicators/indicators/renko.py
index b06e12c1..17dbc872 100644
--- a/stock_indicators/indicators/renko.py
+++ b/stock_indicators/indicators/renko.py
@@ -2,16 +2,17 @@
from typing import Iterable, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.enums import EndType
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_renko(quotes: Iterable[Quote], brick_size: float,
- end_type: EndType = EndType.CLOSE):
+def get_renko(
+ quotes: Iterable[Quote], brick_size: float, end_type: EndType = EndType.CLOSE
+):
"""Get Renko Chart calculated.
Renko Chart is a modified Japanese candlestick pattern
@@ -35,13 +36,15 @@ def get_renko(quotes: Iterable[Quote], brick_size: float,
- [Renko Chart Reference](https://python.stockindicators.dev/indicators/Renko/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetRenko[Quote](CsList(Quote, quotes), CsDecimal(brick_size),
- end_type.cs_value)
+ results = CsIndicator.GetRenko[Quote](
+ CsList(Quote, quotes), CsDecimal(brick_size), end_type.cs_value
+ )
return RenkoResults(results, RenkoResult)
-def get_renko_atr(quotes: Iterable[Quote], atr_periods: int,
- end_type: EndType = EndType.CLOSE):
+def get_renko_atr(
+ quotes: Iterable[Quote], atr_periods: int, end_type: EndType = EndType.CLOSE
+):
"""Get ATR Renko Chart calculated.
The ATR Renko Chart is a modified Japanese candlestick pattern
@@ -65,8 +68,9 @@ def get_renko_atr(quotes: Iterable[Quote], atr_periods: int,
- [ATR Renko Chart Reference](https://python.stockindicators.dev/indicators/Renko/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetRenkoAtr[Quote](CsList(Quote, quotes), atr_periods,
- end_type.cs_value)
+ results = CsIndicator.GetRenkoAtr[Quote](
+ CsList(Quote, quotes), atr_periods, end_type.cs_value
+ )
return RenkoResults(results, RenkoResult)
@@ -125,6 +129,8 @@ def is_up(self, value):
_T = TypeVar("_T", bound=RenkoResult)
+
+
class RenkoResults(IndicatorResults[_T]):
"""
A wrapper class for the list of Renko Chart results.
diff --git a/stock_indicators/indicators/roc.py b/stock_indicators/indicators/roc.py
index 98d473f4..5738ae61 100644
--- a/stock_indicators/indicators/roc.py
+++ b/stock_indicators/indicators/roc.py
@@ -3,11 +3,15 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_roc(quotes: Iterable[Quote], lookback_periods: int, sma_periods: int = None):
+def get_roc(
+ quotes: Iterable[Quote],
+ lookback_periods: int,
+ sma_periods: Optional[int] = None,
+):
"""Get ROC calculated.
Rate of Change (ROC), also known as Momentum Oscillator, is the percent change
@@ -31,10 +35,18 @@ def get_roc(quotes: Iterable[Quote], lookback_periods: int, sma_periods: int = N
- [ROC Reference](https://python.stockindicators.dev/indicators/Roc/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetRoc[Quote](CsList(Quote, quotes), lookback_periods, sma_periods)
+ results = CsIndicator.GetRoc[Quote](
+ CsList(Quote, quotes), lookback_periods, sma_periods
+ )
return ROCResults(results, ROCResult)
-def get_roc_with_band(quotes: Iterable[Quote], lookback_periods: int, ema_periods: int, std_dev_periods: int):
+
+def get_roc_with_band(
+ quotes: Iterable[Quote],
+ lookback_periods: int,
+ ema_periods: int,
+ std_dev_periods: int,
+):
"""Get ROCWB calculated.
Rate of Change with Bands (ROCWB) is the percent change of Close price
@@ -61,7 +73,9 @@ def get_roc_with_band(quotes: Iterable[Quote], lookback_periods: int, ema_period
- [ROCWB Reference](https://python.stockindicators.dev/indicators/Roc/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetRocWb[Quote](CsList(Quote, quotes), lookback_periods, ema_periods, std_dev_periods)
+ results = CsIndicator.GetRocWb[Quote](
+ CsList(Quote, quotes), lookback_periods, ema_periods, std_dev_periods
+ )
return ROCWBResults(results, ROCWBResult)
@@ -96,6 +110,8 @@ def roc_sma(self, value):
_T = TypeVar("_T", bound=ROCResult)
+
+
class ROCResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of ROC(Rate of Change) results.
@@ -143,6 +159,8 @@ def lower_band(self, value):
_T = TypeVar("_T", bound=ROCWBResult)
+
+
class ROCWBResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of ROC(Rate of Change) with band results.
diff --git a/stock_indicators/indicators/rolling_pivots.py b/stock_indicators/indicators/rolling_pivots.py
index 44267dc0..b25cea42 100644
--- a/stock_indicators/indicators/rolling_pivots.py
+++ b/stock_indicators/indicators/rolling_pivots.py
@@ -2,17 +2,21 @@
from typing import Iterable, Optional, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.enums import PivotPointType
from stock_indicators.indicators.common.helpers import RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_rolling_pivots(quotes: Iterable[Quote], window_periods: int,
- offset_periods: int, point_type: PivotPointType = PivotPointType.STANDARD):
+def get_rolling_pivots(
+ quotes: Iterable[Quote],
+ window_periods: int,
+ offset_periods: int,
+ point_type: PivotPointType = PivotPointType.STANDARD,
+):
"""Get Rolling Pivot Points calculated.
Rolling Pivot Points is a modern update to traditional fixed calendar window Pivot Points.
@@ -39,8 +43,9 @@ def get_rolling_pivots(quotes: Iterable[Quote], window_periods: int,
- [Rolling Pivot Points Reference](https://python.stockindicators.dev/indicators/RollingPivots/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetRollingPivots[Quote](CsList(Quote, quotes), window_periods,
- offset_periods, point_type.cs_value)
+ results = CsIndicator.GetRollingPivots[Quote](
+ CsList(Quote, quotes), window_periods, offset_periods, point_type.cs_value
+ )
return RollingPivotsResults(results, RollingPivotsResult)
@@ -123,6 +128,8 @@ def s4(self, value):
_T = TypeVar("_T", bound=RollingPivotsResult)
+
+
class RollingPivotsResults(RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Rolling Pivot Points results.
diff --git a/stock_indicators/indicators/rsi.py b/stock_indicators/indicators/rsi.py
index a55352d6..f6a37b95 100644
--- a/stock_indicators/indicators/rsi.py
+++ b/stock_indicators/indicators/rsi.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_rsi(quotes: Iterable[Quote], lookback_periods: int = 14):
@@ -47,6 +47,8 @@ def rsi(self, value):
_T = TypeVar("_T", bound=RSIResult)
+
+
class RSIResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of RSI(Relative Strength Index) results.
diff --git a/stock_indicators/indicators/slope.py b/stock_indicators/indicators/slope.py
index 40457b93..5b9708dd 100644
--- a/stock_indicators/indicators/slope.py
+++ b/stock_indicators/indicators/slope.py
@@ -2,12 +2,12 @@
from typing import Iterable, Optional, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_slope(quotes: Iterable[Quote], lookback_periods: int):
@@ -80,7 +80,10 @@ def line(self) -> Optional[Decimal]:
def line(self, value):
self._csdata.Line = CsDecimal(value)
+
_T = TypeVar("_T", bound=SlopeResult)
+
+
class SlopeResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Slope results.
diff --git a/stock_indicators/indicators/sma.py b/stock_indicators/indicators/sma.py
index 8fcd4688..c90cb5a3 100644
--- a/stock_indicators/indicators/sma.py
+++ b/stock_indicators/indicators/sma.py
@@ -4,12 +4,15 @@
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.enums import CandlePart
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_sma(quotes: Iterable[Quote], lookback_periods: int,
- candle_part: CandlePart = CandlePart.CLOSE):
+def get_sma(
+ quotes: Iterable[Quote],
+ lookback_periods: int,
+ candle_part: CandlePart = CandlePart.CLOSE,
+):
"""Get SMA calculated.
Simple Moving Average (SMA) is the average of price over a lookback window.
@@ -32,8 +35,8 @@ def get_sma(quotes: Iterable[Quote], lookback_periods: int,
- [SMA Reference](https://python.stockindicators.dev/indicators/Sma/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- quotes = Quote.use(
- quotes, candle_part) # Error occurs if not assigned to local var.
+ # pylint: disable=no-member # Error occurs if not assigned to local var.
+ quotes = Quote.use(quotes, candle_part)
results = CsIndicator.GetSma(quotes, lookback_periods)
return SMAResults(results, SMAResult)
@@ -61,7 +64,8 @@ def get_sma_analysis(quotes: Iterable[Quote], lookback_periods: int):
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
sma_extended_list = CsIndicator.GetSmaAnalysis[Quote](
- CsList(Quote, quotes), lookback_periods)
+ CsList(Quote, quotes), lookback_periods
+ )
return SMAAnalysisResults(sma_extended_list, SMAAnalysisResult)
@@ -80,6 +84,8 @@ def sma(self, value):
_T = TypeVar("_T", bound=SMAResult)
+
+
class SMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of SMA(Simple Moving Average) results.
@@ -119,6 +125,8 @@ def mape(self, value):
_T = TypeVar("_T", bound=SMAAnalysisResult)
+
+
class SMAAnalysisResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of SMA Analysis results.
diff --git a/stock_indicators/indicators/smi.py b/stock_indicators/indicators/smi.py
index eaea2ffc..cf192ed6 100644
--- a/stock_indicators/indicators/smi.py
+++ b/stock_indicators/indicators/smi.py
@@ -3,13 +3,17 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_smi(quotes: Iterable[Quote], lookback_periods: int = 13,
- first_smooth_periods: int = 25, second_smooth_periods: int = 2,
- signal_periods: int = 3):
+def get_smi(
+ quotes: Iterable[Quote],
+ lookback_periods: int = 13,
+ first_smooth_periods: int = 25,
+ second_smooth_periods: int = 2,
+ signal_periods: int = 3,
+):
"""Get SMI calculated.
Stochastic Momentum Index (SMI) is a double-smoothed variant of
@@ -39,9 +43,13 @@ def get_smi(quotes: Iterable[Quote], lookback_periods: int = 13,
- [SMI Reference](https://python.stockindicators.dev/indicators/Smi/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetSmi[Quote](CsList(Quote, quotes), lookback_periods,
- first_smooth_periods, second_smooth_periods,
- signal_periods)
+ results = CsIndicator.GetSmi[Quote](
+ CsList(Quote, quotes),
+ lookback_periods,
+ first_smooth_periods,
+ second_smooth_periods,
+ signal_periods,
+ )
return SMIResults(results, SMIResult)
@@ -68,6 +76,8 @@ def signal(self, value):
_T = TypeVar("_T", bound=SMIResult)
+
+
class SMIResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Stochastic Momentum Index (SMI) results.
diff --git a/stock_indicators/indicators/smma.py b/stock_indicators/indicators/smma.py
index 10a63c72..0d502e2e 100644
--- a/stock_indicators/indicators/smma.py
+++ b/stock_indicators/indicators/smma.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_smma(quotes: Iterable[Quote], lookback_periods: int):
@@ -47,6 +47,8 @@ def smma(self, value):
_T = TypeVar("_T", bound=SMMAResult)
+
+
class SMMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Smoothed Moving Average (SMMA) results.
diff --git a/stock_indicators/indicators/starc_bands.py b/stock_indicators/indicators/starc_bands.py
index 779d3f85..34f91390 100644
--- a/stock_indicators/indicators/starc_bands.py
+++ b/stock_indicators/indicators/starc_bands.py
@@ -1,15 +1,18 @@
from typing import Iterable, Optional, TypeVar
-from warnings import warn
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_starc_bands(quotes: Iterable[Quote], sma_periods: int = None,
- multiplier: float = 2, atr_periods: int = 10):
+def get_starc_bands(
+ quotes: Iterable[Quote],
+ sma_periods: int = 20,
+ multiplier: float = 2,
+ atr_periods: int = 10,
+):
"""Get STARC Bands calculated.
Stoller Average Range Channel (STARC) Bands, are based
@@ -36,12 +39,9 @@ def get_starc_bands(quotes: Iterable[Quote], sma_periods: int = None,
- [STARC Bands Reference](https://python.stockindicators.dev/indicators/StarcBands/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- if sma_periods is None:
- warn('The default value of sma_periods will be removed in the next version. Pass sma_periods explicitly.', DeprecationWarning, stacklevel=2)
- sma_periods = 20
-
- results = CsIndicator.GetStarcBands[Quote](CsList(Quote, quotes), sma_periods,
- multiplier, atr_periods)
+ results = CsIndicator.GetStarcBands[Quote](
+ CsList(Quote, quotes), sma_periods, multiplier, atr_periods
+ )
return STARCBandsResults(results, STARCBandsResult)
@@ -76,6 +76,8 @@ def lower_band(self, value):
_T = TypeVar("_T", bound=STARCBandsResult)
+
+
class STARCBandsResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Stoller Average Range Channel (STARC) Bands results.
diff --git a/stock_indicators/indicators/stc.py b/stock_indicators/indicators/stc.py
index 92217822..edec7e4a 100644
--- a/stock_indicators/indicators/stc.py
+++ b/stock_indicators/indicators/stc.py
@@ -3,12 +3,16 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_stc(quotes: Iterable[Quote], cycle_periods: int = 10,
- fast_periods: int = 23, slow_periods: int = 50):
+def get_stc(
+ quotes: Iterable[Quote],
+ cycle_periods: int = 10,
+ fast_periods: int = 23,
+ slow_periods: int = 50,
+):
"""Get STC calculated.
Schaff Trend Cycle (STC) is a stochastic oscillator view
@@ -35,8 +39,9 @@ def get_stc(quotes: Iterable[Quote], cycle_periods: int = 10,
- [STC Reference](https://python.stockindicators.dev/indicators/Stc/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetStc[Quote](CsList(Quote, quotes), cycle_periods,
- fast_periods, slow_periods)
+ results = CsIndicator.GetStc[Quote](
+ CsList(Quote, quotes), cycle_periods, fast_periods, slow_periods
+ )
return STCResults(results, STCResult)
@@ -55,6 +60,8 @@ def stc(self, value):
_T = TypeVar("_T", bound=STCResult)
+
+
class STCResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Schaff Trend Cycle (STC) results.
diff --git a/stock_indicators/indicators/stdev.py b/stock_indicators/indicators/stdev.py
index d7a3c767..038a0873 100644
--- a/stock_indicators/indicators/stdev.py
+++ b/stock_indicators/indicators/stdev.py
@@ -3,12 +3,13 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_stdev(quotes: Iterable[Quote], lookback_periods: int,
- sma_periods: Optional[int] = None):
+def get_stdev(
+ quotes: Iterable[Quote], lookback_periods: int, sma_periods: Optional[int] = None
+):
"""Get Rolling Standard Deviation calculated.
Rolling Standard Deviation of Close price over a lookback window.
@@ -31,7 +32,9 @@ def get_stdev(quotes: Iterable[Quote], lookback_periods: int,
- [Stdev Reference](https://python.stockindicators.dev/indicators/StdDev/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetStdDev[Quote](CsList(Quote, quotes), lookback_periods, sma_periods)
+ results = CsIndicator.GetStdDev[Quote](
+ CsList(Quote, quotes), lookback_periods, sma_periods
+ )
return StdevResults(results, StdevResult)
@@ -74,6 +77,8 @@ def stdev_sma(self, value):
_T = TypeVar("_T", bound=StdevResult)
+
+
class StdevResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Rolling Standard Deviation results.
diff --git a/stock_indicators/indicators/stdev_channels.py b/stock_indicators/indicators/stdev_channels.py
index ee62a96d..5e759f99 100644
--- a/stock_indicators/indicators/stdev_channels.py
+++ b/stock_indicators/indicators/stdev_channels.py
@@ -3,13 +3,15 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_stdev_channels(quotes: Iterable[Quote],
- lookback_periods: Optional[int] = 20,
- standard_deviations: float = 2):
+def get_stdev_channels(
+ quotes: Iterable[Quote],
+ lookback_periods: Optional[int] = 20,
+ standard_deviations: float = 2,
+):
"""Get Standard Deviation Channels calculated.
Standard Deviation Channels are based on an linearregression centerline
@@ -33,7 +35,9 @@ def get_stdev_channels(quotes: Iterable[Quote],
- [Stdev Channels Reference](https://python.stockindicators.dev/indicators/StdDevChannels/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetStdDevChannels[Quote](CsList(Quote, quotes), lookback_periods, standard_deviations)
+ results = CsIndicator.GetStdDevChannels[Quote](
+ CsList(Quote, quotes), lookback_periods, standard_deviations
+ )
return StdevChannelsResults(results, StdevChannelsResult)
@@ -76,6 +80,8 @@ def break_point(self, value):
_T = TypeVar("_T", bound=StdevChannelsResult)
+
+
class StdevChannelsResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Standard Deviation Channels results.
diff --git a/stock_indicators/indicators/stoch.py b/stock_indicators/indicators/stoch.py
index 071587bb..2551699e 100644
--- a/stock_indicators/indicators/stoch.py
+++ b/stock_indicators/indicators/stoch.py
@@ -4,12 +4,19 @@
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.enums import MAType
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_stoch(quotes: Iterable[Quote], lookback_periods: int = 14, signal_periods: int = 3, smooth_periods: int = 3,
- k_factor: float = 3, d_factor: float = 2, ma_type: MAType = MAType.SMA):
+def get_stoch(
+ quotes: Iterable[Quote],
+ lookback_periods: int = 14, # pylint: disable=too-many-positional-arguments
+ signal_periods: int = 3,
+ smooth_periods: int = 3,
+ k_factor: float = 3,
+ d_factor: float = 2,
+ ma_type: MAType = MAType.SMA,
+):
"""Get Stochastic Oscillator calculated, with KDJ indexes.
Stochastic Oscillatoris a momentum indicator that looks back N periods to produce a scale of 0 to 100.
@@ -47,8 +54,15 @@ def get_stoch(quotes: Iterable[Quote], lookback_periods: int = 14, signal_period
- [Stochastic Oscillator Reference](https://python.stockindicators.dev/indicators/Stoch/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- stoch_results = CsIndicator.GetStoch[Quote](CsList(Quote, quotes), lookback_periods, signal_periods, smooth_periods,
- k_factor, d_factor, ma_type.cs_value)
+ stoch_results = CsIndicator.GetStoch[Quote](
+ CsList(Quote, quotes),
+ lookback_periods,
+ signal_periods,
+ smooth_periods,
+ k_factor,
+ d_factor,
+ ma_type.cs_value,
+ )
return StochResults(stoch_results, StochResult)
@@ -87,6 +101,8 @@ def percent_j(self, value):
_T = TypeVar("_T", bound=StochResult)
+
+
class StochResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Stochastic Oscillator(with KDJ Index) results.
diff --git a/stock_indicators/indicators/stoch_rsi.py b/stock_indicators/indicators/stoch_rsi.py
index 98221ca3..cf801555 100644
--- a/stock_indicators/indicators/stoch_rsi.py
+++ b/stock_indicators/indicators/stoch_rsi.py
@@ -3,11 +3,17 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_stoch_rsi(quotes: Iterable[Quote], rsi_periods: int, stoch_periods: int, signal_periods: int, smooth_periods: int = 1):
+def get_stoch_rsi(
+ quotes: Iterable[Quote],
+ rsi_periods: int,
+ stoch_periods: int,
+ signal_periods: int,
+ smooth_periods: int = 1,
+):
"""Get Stochastic RSI calculated.
Stochastic RSI is a Stochastic interpretation of the Relative Strength Index.
@@ -36,7 +42,13 @@ def get_stoch_rsi(quotes: Iterable[Quote], rsi_periods: int, stoch_periods: int,
- [Stochastic RSI Reference](https://python.stockindicators.dev/indicators/StochRsi/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- stoch_rsi_results = CsIndicator.GetStochRsi[Quote](CsList(Quote, quotes), rsi_periods, stoch_periods, signal_periods, smooth_periods)
+ stoch_rsi_results = CsIndicator.GetStochRsi[Quote](
+ CsList(Quote, quotes),
+ rsi_periods,
+ stoch_periods,
+ signal_periods,
+ smooth_periods,
+ )
return StochRSIResults(stoch_rsi_results, StochRSIResult)
@@ -63,6 +75,8 @@ def signal(self, value):
_T = TypeVar("_T", bound=StochRSIResult)
+
+
class StochRSIResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Stochastic RSI results.
diff --git a/stock_indicators/indicators/super_trend.py b/stock_indicators/indicators/super_trend.py
index 012d237b..6f16aa77 100644
--- a/stock_indicators/indicators/super_trend.py
+++ b/stock_indicators/indicators/super_trend.py
@@ -2,15 +2,17 @@
from typing import Iterable, Optional, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_super_trend(quotes: Iterable[Quote], lookback_periods: int = 10, multiplier: float = 3):
+def get_super_trend(
+ quotes: Iterable[Quote], lookback_periods: int = 10, multiplier: float = 3
+):
"""Get SuperTrend calculated.
SuperTrend attempts to determine the primary trend of Close prices by using
@@ -35,7 +37,9 @@ def get_super_trend(quotes: Iterable[Quote], lookback_periods: int = 10, multipl
- [SuperTrend Reference](https://python.stockindicators.dev/indicators/SuperTrend/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- super_trend_results = CsIndicator.GetSuperTrend[Quote](CsList(Quote, quotes), lookback_periods, multiplier)
+ super_trend_results = CsIndicator.GetSuperTrend[Quote](
+ CsList(Quote, quotes), lookback_periods, multiplier
+ )
return SuperTrendResults(super_trend_results, SuperTrendResult)
@@ -70,6 +74,8 @@ def lower_band(self, value):
_T = TypeVar("_T", bound=SuperTrendResult)
+
+
class SuperTrendResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Super Trend results.
diff --git a/stock_indicators/indicators/t3.py b/stock_indicators/indicators/t3.py
index 4cea9652..fbd0e276 100644
--- a/stock_indicators/indicators/t3.py
+++ b/stock_indicators/indicators/t3.py
@@ -3,12 +3,13 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_t3(quotes: Iterable[Quote], lookback_periods: int = 5,
- volume_factor: float = 0.7):
+def get_t3(
+ quotes: Iterable[Quote], lookback_periods: int = 5, volume_factor: float = 0.7
+):
"""Get T3 calculated.
Tillson T3 is a smooth moving average that reduces
@@ -32,8 +33,9 @@ def get_t3(quotes: Iterable[Quote], lookback_periods: int = 5,
- [T3 Reference](https://python.stockindicators.dev/indicators/T3/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetT3[Quote](CsList(Quote, quotes), lookback_periods,
- volume_factor)
+ results = CsIndicator.GetT3[Quote](
+ CsList(Quote, quotes), lookback_periods, volume_factor
+ )
return T3Results(results, T3Result)
@@ -52,6 +54,8 @@ def t3(self, value):
_T = TypeVar("_T", bound=T3Result)
+
+
class T3Results(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Tillson T3 results.
diff --git a/stock_indicators/indicators/tema.py b/stock_indicators/indicators/tema.py
index 1cbf93c8..f856e2e3 100644
--- a/stock_indicators/indicators/tema.py
+++ b/stock_indicators/indicators/tema.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_tema(quotes: Iterable[Quote], lookback_periods: int):
@@ -47,6 +47,8 @@ def tema(self, value):
_T = TypeVar("_T", bound=TEMAResult)
+
+
class TEMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Triple Exponential Moving Average (TEMA) results.
diff --git a/stock_indicators/indicators/tr.py b/stock_indicators/indicators/tr.py
index e5b260f9..90b99456 100644
--- a/stock_indicators/indicators/tr.py
+++ b/stock_indicators/indicators/tr.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_tr(quotes: Iterable[Quote]):
@@ -43,6 +43,8 @@ def tr(self, value):
_T = TypeVar("_T", bound=TrResult)
+
+
class TrResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of True Range (TR) results.
diff --git a/stock_indicators/indicators/trix.py b/stock_indicators/indicators/trix.py
index 4ce81b96..1d103070 100644
--- a/stock_indicators/indicators/trix.py
+++ b/stock_indicators/indicators/trix.py
@@ -3,11 +3,13 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_trix(quotes: Iterable[Quote], lookback_periods: int, signal_periods: Optional[int] = None):
+def get_trix(
+ quotes: Iterable[Quote], lookback_periods: int, signal_periods: Optional[int] = None
+):
"""Get TRIX calculated.
Triple EMA Oscillator (TRIX) is the rate of change for a 3 EMA smoothing of the Close price over a lookback window.
@@ -31,7 +33,9 @@ def get_trix(quotes: Iterable[Quote], lookback_periods: int, signal_periods: Opt
- [TRIX Reference](https://python.stockindicators.dev/indicators/Trix/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetTrix[Quote](CsList(Quote, quotes), lookback_periods, signal_periods)
+ results = CsIndicator.GetTrix[Quote](
+ CsList(Quote, quotes), lookback_periods, signal_periods
+ )
return TRIXResults(results, TRIXResult)
@@ -66,6 +70,8 @@ def signal(self, value):
_T = TypeVar("_T", bound=TRIXResult)
+
+
class TRIXResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Triple EMA Oscillator (TRIX) results.
diff --git a/stock_indicators/indicators/tsi.py b/stock_indicators/indicators/tsi.py
index 128e6172..c87690ca 100644
--- a/stock_indicators/indicators/tsi.py
+++ b/stock_indicators/indicators/tsi.py
@@ -3,12 +3,16 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_tsi(quotes: Iterable[Quote], lookback_periods: int = 25,
- smooth_periods: int = 13, signal_periods: int = 7):
+def get_tsi(
+ quotes: Iterable[Quote],
+ lookback_periods: int = 25,
+ smooth_periods: int = 13,
+ signal_periods: int = 7,
+):
"""Get TSI calculated.
True Strength Index (TSI) is a momentum oscillator
@@ -35,8 +39,9 @@ def get_tsi(quotes: Iterable[Quote], lookback_periods: int = 25,
- [TSI Reference](https://python.stockindicators.dev/indicators/Tsi/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetTsi[Quote](CsList(Quote, quotes), lookback_periods,
- smooth_periods, signal_periods)
+ results = CsIndicator.GetTsi[Quote](
+ CsList(Quote, quotes), lookback_periods, smooth_periods, signal_periods
+ )
return TSIResults(results, TSIResult)
@@ -63,6 +68,8 @@ def signal(self, value):
_T = TypeVar("_T", bound=TSIResult)
+
+
class TSIResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of True Strength Index (TSI) results.
diff --git a/stock_indicators/indicators/ulcer_index.py b/stock_indicators/indicators/ulcer_index.py
index 92e859ec..780fd428 100644
--- a/stock_indicators/indicators/ulcer_index.py
+++ b/stock_indicators/indicators/ulcer_index.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_ulcer_index(quotes: Iterable[Quote], lookback_periods: int = 14):
@@ -47,6 +47,8 @@ def ui(self, value):
_T = TypeVar("_T", bound=UlcerIndexResult)
+
+
class UlcerIndexResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Ulcer Index (UI) results.
diff --git a/stock_indicators/indicators/ultimate.py b/stock_indicators/indicators/ultimate.py
index 88033fa8..855e71fc 100644
--- a/stock_indicators/indicators/ultimate.py
+++ b/stock_indicators/indicators/ultimate.py
@@ -3,12 +3,16 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_ultimate(quotes: Iterable[Quote], short_periods: int = 7,
- middle_periods: int = 14, long_periods: int = 28):
+def get_ultimate(
+ quotes: Iterable[Quote],
+ short_periods: int = 7,
+ middle_periods: int = 14,
+ long_periods: int = 28,
+):
"""Get Ultimate Oscillator calculated.
Ultimate Oscillator uses several lookback periods to weigh buying power
@@ -35,8 +39,9 @@ def get_ultimate(quotes: Iterable[Quote], short_periods: int = 7,
- [Ultimate Oscillator Reference](https://python.stockindicators.dev/indicators/Ultimate/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetUltimate[Quote](CsList(Quote, quotes), short_periods,
- middle_periods, long_periods)
+ results = CsIndicator.GetUltimate[Quote](
+ CsList(Quote, quotes), short_periods, middle_periods, long_periods
+ )
return UltimateResults(results, UltimateResult)
@@ -55,6 +60,8 @@ def ultimate(self, value):
_T = TypeVar("_T", bound=UltimateResult)
+
+
class UltimateResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Ultimate Oscillator results.
diff --git a/stock_indicators/indicators/volatility_stop.py b/stock_indicators/indicators/volatility_stop.py
index ac78d9f0..9196a633 100644
--- a/stock_indicators/indicators/volatility_stop.py
+++ b/stock_indicators/indicators/volatility_stop.py
@@ -3,12 +3,13 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_volatility_stop(quotes: Iterable[Quote], lookback_periods: int = 7,
- multiplier: float = 3):
+def get_volatility_stop(
+ quotes: Iterable[Quote], lookback_periods: int = 7, multiplier: float = 3
+):
"""Get Volatility Stop calculated.
Volatility Stop is an ATR based indicator used to
@@ -32,8 +33,9 @@ def get_volatility_stop(quotes: Iterable[Quote], lookback_periods: int = 7,
- [Volatility Stop Reference](https://python.stockindicators.dev/indicators/VolatilityStop/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetVolatilityStop[Quote](CsList(Quote, quotes), lookback_periods,
- multiplier)
+ results = CsIndicator.GetVolatilityStop[Quote](
+ CsList(Quote, quotes), lookback_periods, multiplier
+ )
return VolatilityStopResults(results, VolatilityStopResult)
@@ -76,6 +78,8 @@ def is_stop(self, value):
_T = TypeVar("_T", bound=VolatilityStopResult)
+
+
class VolatilityStopResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Volatility Stop results.
diff --git a/stock_indicators/indicators/vortex.py b/stock_indicators/indicators/vortex.py
index 3ace0fe0..f2c31f72 100644
--- a/stock_indicators/indicators/vortex.py
+++ b/stock_indicators/indicators/vortex.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_vortex(quotes: Iterable[Quote], lookback_periods: int):
@@ -56,6 +56,8 @@ def nvi(self, value):
_T = TypeVar("_T", bound=VortexResult)
+
+
class VortexResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Vortex Indicator (VI) results.
diff --git a/stock_indicators/indicators/vwap.py b/stock_indicators/indicators/vwap.py
index cec0e32e..c74ef86d 100644
--- a/stock_indicators/indicators/vwap.py
+++ b/stock_indicators/indicators/vwap.py
@@ -1,21 +1,41 @@
from datetime import datetime
-from typing import Iterable, Optional, TypeVar, overload
+from typing import Iterable, Optional, TypeVar, Union, overload
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import DateTime as CsDateTime
+from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
@overload
-def get_vwap(quotes: Iterable[Quote], start: Optional[datetime] = None) -> "VWAPResults[VWAPResult]": ...
+def get_vwap(
+ quotes: Iterable[Quote], start: Optional[datetime] = None
+) -> "VWAPResults[VWAPResult]": ...
+
+
@overload
-def get_vwap(quotes: Iterable[Quote], year: int,
- month: int = 1, day: int = 1,
- hour: int = 0, minute: int = 0) -> "VWAPResults[VWAPResult]": ...
-def get_vwap(quotes, start = None, month = 1, day = 1, hour = 0, minute = 0):
+def get_vwap(
+ quotes: Iterable[Quote],
+ year: int,
+ *,
+ month: int = 1,
+ day: int = 1,
+ hour: int = 0,
+ minute: int = 0,
+) -> "VWAPResults[VWAPResult]": ...
+
+
+def get_vwap(
+ quotes: Iterable[Quote],
+ start: Union[datetime, int, None] = None,
+ *legacy_date_parts,
+ month: int = 1,
+ day: int = 1,
+ hour: int = 0,
+ minute: int = 0,
+) -> "VWAPResults[VWAPResult]": # pylint: disable=too-many-branches,too-many-statements,keyword-arg-before-vararg
"""Get VWAP calculated.
Volume Weighted Average Price (VWAP) is a Volume weighted average
@@ -39,10 +59,81 @@ def get_vwap(quotes, start = None, month = 1, day = 1, hour = 0, minute = 0):
- [VWAP Fractal Reference](https://python.stockindicators.dev/indicators/Vwap/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- if isinstance(start, int):
- start = datetime(start, month, day, hour, minute)
-
- results = CsIndicator.GetVwap[Quote](CsList(Quote, quotes), CsDateTime(start) if start else None)
+ # Backward compatibility: support positional year,month,day,hour as in tests
+ if legacy_date_parts:
+ # Validate legacy_date_parts is a sequence with at most 4 items
+ if not isinstance(legacy_date_parts, (tuple, list)):
+ raise TypeError("legacy_date_parts must be a sequence")
+ if len(legacy_date_parts) > 4:
+ raise ValueError(
+ "Too many positional date arguments (max 4: month, day, hour, minute)"
+ )
+
+ # Interpret: start is actually the year; legacy_date_parts supply remaining
+ if not isinstance(start, int):
+ raise TypeError(
+ "Year must be provided as an int when using legacy positional date form"
+ )
+ year = start
+
+ # Fill provided parts into month, day, hour, minute if given positionally
+ parts = list(legacy_date_parts)
+ if len(parts) > 0:
+ if not isinstance(parts[0], int):
+ raise TypeError("Month must be an int")
+ month = parts[0]
+ if len(parts) > 1:
+ if not isinstance(parts[1], int):
+ raise TypeError("Day must be an int")
+ day = parts[1]
+ if len(parts) > 2:
+ if not isinstance(parts[2], int):
+ raise TypeError("Hour must be an int")
+ hour = parts[2]
+ if len(parts) > 3:
+ if not isinstance(parts[3], int):
+ raise TypeError("Minute must be an int")
+ minute = parts[3]
+
+ # Validate ranges
+ if not 1 <= month <= 12:
+ raise ValueError(f"Month must be 1-12, got {month}")
+ if not 1 <= day <= 31:
+ raise ValueError(f"Day must be 1-31, got {day}")
+ if not 0 <= hour <= 23:
+ raise ValueError(f"Hour must be 0-23, got {hour}")
+ if not 0 <= minute <= 59:
+ raise ValueError(f"Minute must be 0-59, got {minute}")
+
+ start_dt = datetime(year, month, day, hour, minute)
+ else:
+ # When not using legacy parts, validate that start is either int (year) or datetime
+ if start is not None:
+ if isinstance(start, int):
+ # Using keyword arguments for date parts
+ year = start
+ # Validate ranges for keyword arguments
+ if not 1 <= month <= 12:
+ raise ValueError(f"Month must be 1-12, got {month}")
+ if not 1 <= day <= 31:
+ raise ValueError(f"Day must be 1-31, got {day}")
+ if not 0 <= hour <= 23:
+ raise ValueError(f"Hour must be 0-23, got {hour}")
+ if not 0 <= minute <= 59:
+ raise ValueError(f"Minute must be 0-59, got {minute}")
+ start_dt = datetime(year, month, day, hour, minute)
+ elif isinstance(start, datetime):
+ start_dt = start
+ else:
+ raise TypeError(
+ f"start must be int (year), datetime, or None, got {type(start).__name__}"
+ )
+ else:
+ start_dt = None
+
+ results = CsIndicator.GetVwap[Quote](
+ CsList(Quote, quotes), CsDateTime(start_dt) if start_dt else None
+ )
return VWAPResults(results, VWAPResult)
@@ -61,6 +152,8 @@ def vwap(self, value):
_T = TypeVar("_T", bound=VWAPResult)
+
+
class VWAPResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Volume Weighted Average Price (VWAP) results.
diff --git a/stock_indicators/indicators/vwma.py b/stock_indicators/indicators/vwma.py
index 9df7abfc..fd1c642f 100644
--- a/stock_indicators/indicators/vwma.py
+++ b/stock_indicators/indicators/vwma.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_vwma(quotes: Iterable[Quote], lookback_periods: int):
@@ -47,6 +47,8 @@ def vwma(self, value):
_T = TypeVar("_T", bound=VWMAResult)
+
+
class VWMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Volume Weighted Moving Average (VWMA) results.
diff --git a/stock_indicators/indicators/williams_r.py b/stock_indicators/indicators/williams_r.py
index 2276898c..f739649b 100644
--- a/stock_indicators/indicators/williams_r.py
+++ b/stock_indicators/indicators/williams_r.py
@@ -3,8 +3,8 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
def get_williams_r(quotes: Iterable[Quote], lookback_periods: int = 14):
@@ -48,6 +48,8 @@ def williams_r(self, value):
_T = TypeVar("_T", bound=WilliamsResult)
+
+
class WilliamsResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Williams %R results.
diff --git a/stock_indicators/indicators/wma.py b/stock_indicators/indicators/wma.py
index dc34b1f6..07eb0984 100644
--- a/stock_indicators/indicators/wma.py
+++ b/stock_indicators/indicators/wma.py
@@ -3,12 +3,15 @@
from stock_indicators._cslib import CsIndicator
from stock_indicators.indicators.common.enums import CandlePart
from stock_indicators.indicators.common.helpers import CondenseMixin, RemoveWarmupMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_wma(quotes: Iterable[Quote], lookback_periods: int,
- candle_part: CandlePart = CandlePart.CLOSE):
+def get_wma(
+ quotes: Iterable[Quote],
+ lookback_periods: int,
+ candle_part: CandlePart = CandlePart.CLOSE,
+):
"""Get WMA calculated.
Weighted Moving Average (WMA) is the linear weighted average
@@ -33,7 +36,7 @@ def get_wma(quotes: Iterable[Quote], lookback_periods: int,
- [WMA Reference](https://python.stockindicators.dev/indicators/Wma/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- quotes = Quote.use(quotes, candle_part) # Error occurs if not assigned to local var.
+ quotes = Quote.use(quotes, candle_part) # pylint: disable=no-member # Error occurs if not assigned to local var.
results = CsIndicator.GetWma(quotes, lookback_periods)
return WMAResults(results, WMAResult)
@@ -53,6 +56,8 @@ def wma(self, value):
_T = TypeVar("_T", bound=WMAResult)
+
+
class WMAResults(CondenseMixin, RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Weighted Moving Average (WMA) results.
diff --git a/stock_indicators/indicators/zig_zag.py b/stock_indicators/indicators/zig_zag.py
index 7ccb2165..bd6fcf44 100644
--- a/stock_indicators/indicators/zig_zag.py
+++ b/stock_indicators/indicators/zig_zag.py
@@ -2,17 +2,20 @@
from typing import Iterable, Optional, TypeVar
from stock_indicators._cslib import CsIndicator
-from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import Decimal as CsDecimal
+from stock_indicators._cstypes import List as CsList
from stock_indicators._cstypes import to_pydecimal
from stock_indicators.indicators.common.enums import EndType
from stock_indicators.indicators.common.helpers import CondenseMixin
-from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote
+from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
-def get_zig_zag(quotes: Iterable[Quote], end_type: EndType = EndType.CLOSE,
- percent_change: float = 5):
+def get_zig_zag(
+ quotes: Iterable[Quote],
+ end_type: EndType = EndType.CLOSE,
+ percent_change: float = 5,
+):
"""Get Zig Zag calculated.
Zig Zag is a price chart overlay that simplifies the up and down
@@ -36,8 +39,9 @@ def get_zig_zag(quotes: Iterable[Quote], end_type: EndType = EndType.CLOSE,
- [Zig Zag Reference](https://python.stockindicators.dev/indicators/ZigZag/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
- results = CsIndicator.GetZigZag[Quote](CsList(Quote, quotes), end_type.cs_value,
- CsDecimal(percent_change))
+ results = CsIndicator.GetZigZag[Quote](
+ CsList(Quote, quotes), end_type.cs_value, CsDecimal(percent_change)
+ )
return ZigZagResults(results, ZigZagResult)
@@ -80,6 +84,8 @@ def retrace_low(self, value):
_T = TypeVar("_T", bound=ZigZagResult)
+
+
class ZigZagResults(CondenseMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Zig Zag results.
diff --git a/tests/common/test-dateof-roundtrip-variants.py b/tests/common/test-dateof-roundtrip-variants.py
index 38c68e72..657a273d 100644
--- a/tests/common/test-dateof-roundtrip-variants.py
+++ b/tests/common/test-dateof-roundtrip-variants.py
@@ -47,7 +47,7 @@ def _mk_date_only():
@pytest.mark.parametrize(
- "variant, maker",
+ ("variant", "maker"),
[
("utc", _mk_utc),
("offset", _mk_offset),
@@ -55,8 +55,9 @@ def _mk_date_only():
("date_only", _mk_date_only),
],
)
-def test_sma_roundtrip_dates_variants(_variant, maker):
+def test_sma_roundtrip_dates_variants(variant, maker):
quotes = maker()
+ assert variant
results = indicators.get_sma(quotes, 2)
assert len(results) == len(quotes)
diff --git a/tests/common/test_candle.py b/tests/common/test_candle.py
index 7653a529..7fb02b2c 100644
--- a/tests/common/test_candle.py
+++ b/tests/common/test_candle.py
@@ -1,41 +1,42 @@
from stock_indicators import indicators
+
class TestCandleResults:
def test_standard(self, quotes):
results = indicators.get_doji(quotes, 0.1)
-
+
r = results[0]
assert 212.80 == round(float(r.candle.close), 2)
- assert 1.83 == round(float(r.candle.size), 2)
- assert 0.19 == round(float(r.candle.body), 2)
- assert 0.10 == round(float(r.candle.body_pct), 2)
- assert 1.09 == round(float(r.candle.lower_wick), 2)
- assert 0.60 == round(float(r.candle.lower_wick_pct), 2)
- assert 0.55 == round(float(r.candle.upper_wick), 2)
- assert 0.30 == round(float(r.candle.upper_wick_pct), 2)
- assert r.candle.is_bearish == False
- assert r.candle.is_bullish == True
-
+ assert 1.83 == round(float(r.candle.size), 2)
+ assert 0.19 == round(float(r.candle.body), 2)
+ assert 0.10 == round(float(r.candle.body_pct), 2)
+ assert 1.09 == round(float(r.candle.lower_wick), 2)
+ assert 0.60 == round(float(r.candle.lower_wick_pct), 2)
+ assert 0.55 == round(float(r.candle.upper_wick), 2)
+ assert 0.30 == round(float(r.candle.upper_wick_pct), 2)
+ assert not r.candle.is_bearish
+ assert r.candle.is_bullish
+
r = results[351]
assert 263.16 == round(float(r.candle.close), 2)
- assert 1.24 == round(float(r.candle.size), 2)
- assert 0.00 == round(float(r.candle.body), 2)
- assert 0.00 == round(float(r.candle.body_pct), 2)
- assert 0.55 == round(float(r.candle.lower_wick), 2)
- assert 0.44 == round(float(r.candle.lower_wick_pct), 2)
- assert 0.69 == round(float(r.candle.upper_wick), 2)
- assert 0.56 == round(float(r.candle.upper_wick_pct), 2)
- assert r.candle.is_bearish == False
- assert r.candle.is_bullish == False
-
- r = results[501]
+ assert 1.24 == round(float(r.candle.size), 2)
+ assert 0.00 == round(float(r.candle.body), 2)
+ assert 0.00 == round(float(r.candle.body_pct), 2)
+ assert 0.55 == round(float(r.candle.lower_wick), 2)
+ assert 0.44 == round(float(r.candle.lower_wick_pct), 2)
+ assert 0.69 == round(float(r.candle.upper_wick), 2)
+ assert 0.56 == round(float(r.candle.upper_wick_pct), 2)
+ assert not r.candle.is_bearish
+ assert not r.candle.is_bullish
+
+ r = results[501]
assert 245.28 == round(float(r.candle.close), 2)
- assert 2.67 == round(float(r.candle.size), 2)
- assert 0.36 == round(float(r.candle.body), 2)
- assert 0.13 == round(float(r.candle.body_pct), 2)
- assert 2.05 == round(float(r.candle.lower_wick), 2)
- assert 0.77 == round(float(r.candle.lower_wick_pct), 2)
- assert 0.26 == round(float(r.candle.upper_wick), 2)
- assert 0.10 == round(float(r.candle.upper_wick_pct), 2)
- assert r.candle.is_bearish == False
- assert r.candle.is_bullish == True
+ assert 2.67 == round(float(r.candle.size), 2)
+ assert 0.36 == round(float(r.candle.body), 2)
+ assert 0.13 == round(float(r.candle.body_pct), 2)
+ assert 2.05 == round(float(r.candle.lower_wick), 2)
+ assert 0.77 == round(float(r.candle.lower_wick_pct), 2)
+ assert 0.26 == round(float(r.candle.upper_wick), 2)
+ assert 0.10 == round(float(r.candle.upper_wick_pct), 2)
+ assert not r.candle.is_bearish
+ assert r.candle.is_bullish
diff --git a/tests/common/test_common.py b/tests/common/test_common.py
index d52df668..afe46266 100644
--- a/tests/common/test_common.py
+++ b/tests/common/test_common.py
@@ -1,25 +1,28 @@
from datetime import datetime
+
from stock_indicators import indicators
+
class TestCommon:
def test_find(self, quotes):
results = indicators.get_bollinger_bands(quotes)
-
+
r = results.find(datetime(2018, 12, 28))
+ assert r is not None
assert 252.9625 == round(float(r.sma), 4)
assert 230.3495 == round(float(r.lower_band), 4)
-
+
r = results.find(datetime(2018, 12, 31))
+ assert r is not None
assert 251.8600 == round(float(r.sma), 4)
assert 230.0196 == round(float(r.lower_band), 4)
-
-
+
def test_remove_warmup_periods(self, quotes):
results = indicators.get_adl(quotes)
assert 502 == len(results)
-
+
results = results.remove_warmup_periods(200)
assert 302 == len(results)
-
+
results = results.remove_warmup_periods(1000)
assert 0 == len(results)
diff --git a/tests/common/test_cstype_conversion.py b/tests/common/test_cstype_conversion.py
index bfeb832c..fb23d473 100644
--- a/tests/common/test_cstype_conversion.py
+++ b/tests/common/test_cstype_conversion.py
@@ -1,12 +1,13 @@
from datetime import datetime
-from numbers import Number
from decimal import Decimal as PyDecimal
+from numbers import Number
from stock_indicators._cslib import CsCultureInfo
from stock_indicators._cstypes import DateTime as CsDateTime
from stock_indicators._cstypes import Decimal as CsDecimal
from stock_indicators._cstypes import to_pydatetime, to_pydecimal
+
class TestCsTypeConversion:
def test_datetime_conversion(self):
py_datetime = datetime.now()
@@ -22,7 +23,9 @@ def test_datetime_conversion(self):
# assert py_datetime.microsecond == converted_datetime.microsecond
def test_timezone_aware_datetime_conversion(self):
- py_datetime = datetime.strptime('2022-06-02 10:29:00-04:00', '%Y-%m-%d %H:%M:%S%z')
+ py_datetime = datetime.strptime(
+ "2022-06-02 10:29:00-04:00", "%Y-%m-%d %H:%M:%S%z"
+ )
converted_datetime = to_pydatetime(CsDateTime(py_datetime))
assert py_datetime.year == converted_datetime.year
@@ -38,40 +41,41 @@ def test_timezone_aware_datetime_conversion(self):
def test_auto_conversion_from_double_to_float(self):
from System import Double as CsDouble
- cs_double = CsDouble.Parse('1996.1012', CsCultureInfo.InvariantCulture)
+ cs_double = CsDouble.Parse("1996.1012", CsCultureInfo.InvariantCulture)
assert isinstance(cs_double, Number)
assert isinstance(cs_double, float)
assert 1996.1012 == cs_double
def test_quote_constructor_retains_timezone(self):
- from stock_indicators.indicators.common.quote import Quote
from decimal import Decimal
-
- dt = datetime.fromisoformat('2000-03-26T23:00:00+00:00')
+
+ from stock_indicators.indicators.common.quote import Quote
+
+ dt = datetime.fromisoformat("2000-03-26T23:00:00+00:00")
q = Quote(
date=dt,
- open=Decimal('23'),
- high=Decimal('26'),
- low=Decimal('20'),
- close=Decimal('25'),
- volume=Decimal('323')
+ open=Decimal("23"),
+ high=Decimal("26"),
+ low=Decimal("20"),
+ close=Decimal("25"),
+ volume=Decimal("323"),
)
- assert str(q.date.tzinfo) == 'UTC'
- assert str(q.date.time()) == '23:00:00'
+ assert str(q.date.tzinfo) == "UTC"
+ assert str(q.date.time()) == "23:00:00"
def test_decimal_conversion(self):
py_decimal = 1996.1012
cs_decimal = CsDecimal(py_decimal)
- assert str(py_decimal) == '1996.1012'
+ assert str(py_decimal) == "1996.1012"
assert to_pydecimal(cs_decimal) == PyDecimal(str(py_decimal))
def test_decimal_conversion_expressed_in_exponential_notation(self):
py_decimal = 0.000018
cs_decimal = CsDecimal(py_decimal)
- assert str(py_decimal) == '1.8e-05'
+ assert str(py_decimal) == "1.8e-05"
assert to_pydecimal(cs_decimal) == PyDecimal(str(py_decimal))
def test_exponential_notation_decimal_conversion(self):
diff --git a/tests/common/test_cstype_datetime_kind.py b/tests/common/test_cstype_datetime_kind.py
index 4337ef1c..aa9b629b 100644
--- a/tests/common/test_cstype_datetime_kind.py
+++ b/tests/common/test_cstype_datetime_kind.py
@@ -1,5 +1,4 @@
from datetime import datetime, timezone
-import pytest
import pytest
@@ -9,27 +8,34 @@
def test_to_pydatetime_sets_utc_for_tzaware():
# Start with an offset-aware datetime and ensure roundtrip yields UTC tz
- dt = datetime.fromisoformat('2022-06-02T10:29:00-04:00')
+ dt = datetime.fromisoformat("2022-06-02T10:29:00-04:00")
expected_utc = dt.astimezone(timezone.utc)
cs_dt = CsDateTime(dt)
py_dt = to_pydatetime(cs_dt)
# tz-aware should come back as UTC
- assert str(py_dt.tzinfo) == 'UTC'
+ assert str(py_dt.tzinfo) == "UTC"
# Time should reflect conversion to UTC
assert py_dt.replace(microsecond=0) == expected_utc.replace(microsecond=0)
def test_to_pydatetime_keeps_naive_naive():
# Naive input should remain naive and preserve second-level components
- dt = datetime.fromisoformat('2023-01-02T03:04:05') # no microseconds
+ dt = datetime.fromisoformat("2023-01-02T03:04:05") # no microseconds
cs_dt = CsDateTime(dt)
py_dt = to_pydatetime(cs_dt)
assert py_dt.tzinfo is None
- assert (py_dt.year, py_dt.month, py_dt.day, py_dt.hour, py_dt.minute, py_dt.second) == (
+ assert (
+ py_dt.year,
+ py_dt.month,
+ py_dt.day,
+ py_dt.hour,
+ py_dt.minute,
+ py_dt.second,
+ ) == (
dt.year,
dt.month,
dt.day,
@@ -41,7 +47,7 @@ def test_to_pydatetime_keeps_naive_naive():
def test_csdatetime_rejects_non_datetime_input():
with pytest.raises(TypeError):
- CsDateTime('2020-01-01') # type: ignore[arg-type]
+ CsDateTime("2020-01-01") # type: ignore[arg-type]
def test_to_pydatetime_handles_system_local_timezone_roundtrip():
@@ -53,5 +59,7 @@ def test_to_pydatetime_handles_system_local_timezone_roundtrip():
py_dt = to_pydatetime(cs_dt)
# Interop always returns tz-aware as UTC; ensure the instant is preserved
- assert str(py_dt.tzinfo) == 'UTC'
- assert py_dt.replace(microsecond=0) == dt_local.astimezone(timezone.utc).replace(microsecond=0)
+ assert str(py_dt.tzinfo) == "UTC"
+ assert py_dt.replace(microsecond=0) == dt_local.astimezone(timezone.utc).replace(
+ microsecond=0
+ )
diff --git a/tests/common/test_dateof_equivalence.py b/tests/common/test_dateof_equivalence.py
index 289bd4ac..5502343e 100644
--- a/tests/common/test_dateof_equivalence.py
+++ b/tests/common/test_dateof_equivalence.py
@@ -55,6 +55,6 @@ def test_equivalent_non_date_columns(name: str):
assert len(rows) == len(ref_rows)
# For each row, all columns except the date column (index 1) should match
- for (ref_row, row) in zip(ref_rows, rows):
+ for ref_row, row in zip(ref_rows, rows):
assert ref_row[0] == row[0] # index
assert ref_row[2:] == row[2:] # all non-date price/volume columns
diff --git a/tests/common/test_dateof_identity_roundtrip.py b/tests/common/test_dateof_identity_roundtrip.py
index 7b9f942a..64c5181a 100644
--- a/tests/common/test_dateof_identity_roundtrip.py
+++ b/tests/common/test_dateof_identity_roundtrip.py
@@ -4,6 +4,7 @@
"""
from datetime import timezone
+
import pytest
from stock_indicators import indicators
diff --git a/tests/common/test_indicator_results.py b/tests/common/test_indicator_results.py
index a5206946..a6be2b73 100644
--- a/tests/common/test_indicator_results.py
+++ b/tests/common/test_indicator_results.py
@@ -1,61 +1,50 @@
from datetime import datetime
-import pytest
from stock_indicators import indicators
+
class TestIndicatorResults:
def test_add_results(self, quotes):
results = indicators.get_sma(quotes, 20)
r4 = results + results + results + results
-
+
assert len(r4) == len(results) * 4
-
+
for i in range(4):
- idx = len(results)*i
- assert r4[18+idx].sma is None
- assert 214.5250 == round(float(r4[19+idx].sma), 4)
- assert 215.0310 == round(float(r4[24+idx].sma), 4)
- assert 234.9350 == round(float(r4[149+idx].sma), 4)
- assert 255.5500 == round(float(r4[249+idx].sma), 4)
- assert 251.8600 == round(float(r4[501+idx].sma), 4)
-
+ idx = len(results) * i
+ assert r4[18 + idx].sma is None
+ assert 214.5250 == round(float(r4[19 + idx].sma), 4)
+ assert 215.0310 == round(float(r4[24 + idx].sma), 4)
+ assert 234.9350 == round(float(r4[149 + idx].sma), 4)
+ assert 255.5500 == round(float(r4[249 + idx].sma), 4)
+ assert 251.8600 == round(float(r4[501 + idx].sma), 4)
+
def test_mul_results(self, quotes):
results = indicators.get_sma(quotes, 20)
r4 = results * 4
-
+
assert len(r4) == len(results) * 4
for i in range(4):
- idx = len(results)*i
- assert r4[18+idx].sma is None
- assert 214.5250 == round(float(r4[19+idx].sma), 4)
- assert 215.0310 == round(float(r4[24+idx].sma), 4)
- assert 234.9350 == round(float(r4[149+idx].sma), 4)
- assert 255.5500 == round(float(r4[249+idx].sma), 4)
- assert 251.8600 == round(float(r4[501+idx].sma), 4)
-
- def test_done_and_reload(self, quotes):
- results = indicators.get_sma(quotes, 20)
- results.done()
-
- with pytest.raises(ValueError):
- results * 2
+ idx = len(results) * i
+ assert r4[18 + idx].sma is None
+ assert 214.5250 == round(float(r4[19 + idx].sma), 4)
+ assert 215.0310 == round(float(r4[24 + idx].sma), 4)
+ assert 234.9350 == round(float(r4[149 + idx].sma), 4)
+ assert 255.5500 == round(float(r4[249 + idx].sma), 4)
+ assert 251.8600 == round(float(r4[501 + idx].sma), 4)
- results.reload()
- r2 = results * 2
-
- assert len(r2) == len(results) * 2
-
def test_find(self, quotes):
results = indicators.get_sma(quotes, 20)
-
+
# r[19]
r = results.find(datetime(2017, 1, 31))
+ assert r is not None
assert 214.5250 == round(float(r.sma), 4)
def test_not_found(self, quotes):
results = indicators.get_sma(quotes, 20)
-
+
# returns None
r = results.find(datetime(1996, 10, 12))
assert r is None
@@ -63,6 +52,6 @@ def test_not_found(self, quotes):
def test_remove_with_period(self, quotes):
results = indicators.get_sma(quotes, 20)
length = len(results)
-
+
results = results.remove_warmup_periods(50)
assert len(results) == length - 50
diff --git a/tests/common/test_locale.py b/tests/common/test_locale.py
index 1856d7af..4b47fe57 100644
--- a/tests/common/test_locale.py
+++ b/tests/common/test_locale.py
@@ -1,5 +1,6 @@
-from decimal import Decimal as PyDecimal
import locale
+from decimal import Decimal as PyDecimal
+
import pytest
from stock_indicators._cslib import CsDecimal
@@ -19,9 +20,7 @@ def _uses_comma_decimal_separator() -> bool:
import clr # type: ignore # noqa: F401
from System.Globalization import CultureInfo # type: ignore
- return (
- CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ","
- )
+ return CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ","
except Exception:
pass
@@ -41,7 +40,10 @@ def _uses_comma_decimal_separator() -> bool:
@pytest.mark.localization
-@pytest.mark.skipif(not uses_comma_decimal, reason="Localization tests require a comma decimal separator culture (e.g., ru-RU)")
+@pytest.mark.skipif(
+ not uses_comma_decimal,
+ reason="Localization tests require a comma decimal separator culture (e.g., ru-RU)",
+)
class TestLocale:
"""
These tests are intended for environments where a comma is used as the decimal separator,
diff --git a/tests/common/test_quote.py b/tests/common/test_quote.py
index cfd3b818..997ccd47 100644
--- a/tests/common/test_quote.py
+++ b/tests/common/test_quote.py
@@ -1,63 +1,64 @@
-from stock_indicators.indicators.common.quote import Quote
-from decimal import Decimal
from datetime import datetime, timezone
+from decimal import Decimal
+
+from stock_indicators.indicators.common.quote import Quote
def test_quote_constructor_retains_timezone():
- dt = datetime.fromisoformat('2000-03-26T23:00:00+00:00')
+ dt = datetime.fromisoformat("2000-03-26T23:00:00+00:00")
q = Quote(
date=dt,
- open=Decimal('23'),
- high=Decimal('26'),
- low=Decimal('20'),
- close=Decimal('25'),
- volume=Decimal('323')
+ open=Decimal("23"),
+ high=Decimal("26"),
+ low=Decimal("20"),
+ close=Decimal("25"),
+ volume=Decimal("323"),
)
- assert str(q.date.tzinfo) == 'UTC'
- assert str(q.date.time()) == '23:00:00'
+ assert str(q.date.tzinfo) == "UTC"
+ assert str(q.date.time()) == "23:00:00"
def test_quote_constructor_handles_various_date_formats():
- dt1 = datetime.fromisoformat('2000-03-26T23:00:00+00:00')
- dt2 = datetime.strptime('2000-03-26 23:00:00', '%Y-%m-%d %H:%M:%S')
- dt3 = datetime.strptime('2000-03-26', '%Y-%m-%d')
+ dt1 = datetime.fromisoformat("2000-03-26T23:00:00+00:00")
+ dt2 = datetime.strptime("2000-03-26 23:00:00", "%Y-%m-%d %H:%M:%S")
+ dt3 = datetime.strptime("2000-03-26", "%Y-%m-%d")
q1 = Quote(
date=dt1,
- open=Decimal('23'),
- high=Decimal('26'),
- low=Decimal('20'),
- close=Decimal('25'),
- volume=Decimal('323')
+ open=Decimal("23"),
+ high=Decimal("26"),
+ low=Decimal("20"),
+ close=Decimal("25"),
+ volume=Decimal("323"),
)
q2 = Quote(
date=dt2,
- open=Decimal('23'),
- high=Decimal('26'),
- low=Decimal('20'),
- close=Decimal('25'),
- volume=Decimal('323')
+ open=Decimal("23"),
+ high=Decimal("26"),
+ low=Decimal("20"),
+ close=Decimal("25"),
+ volume=Decimal("323"),
)
q3 = Quote(
date=dt3,
- open=Decimal('23'),
- high=Decimal('26'),
- low=Decimal('20'),
- close=Decimal('25'),
- volume=Decimal('323')
+ open=Decimal("23"),
+ high=Decimal("26"),
+ low=Decimal("20"),
+ close=Decimal("25"),
+ volume=Decimal("323"),
)
- assert str(q1.date.tzinfo) == 'UTC'
- assert str(q1.date.time()) == '23:00:00'
+ assert str(q1.date.tzinfo) == "UTC"
+ assert str(q1.date.time()) == "23:00:00"
- assert str(q2.date.tzinfo) == 'None'
- assert str(q2.date.time()) == '23:00:00'
+ assert str(q2.date.tzinfo) == "None"
+ assert str(q2.date.time()) == "23:00:00"
- assert str(q3.date.tzinfo) == 'None'
- assert str(q3.date.time()) == '00:00:00'
+ assert str(q3.date.tzinfo) == "None"
+ assert str(q3.date.time()) == "00:00:00"
def test_quote_constructor_handles_system_local_timezone():
@@ -67,14 +68,16 @@ def test_quote_constructor_handles_system_local_timezone():
q = Quote(
date=dt,
- open=Decimal('23'),
- high=Decimal('26'),
- low=Decimal('20'),
- close=Decimal('25'),
- volume=Decimal('323'),
+ open=Decimal("23"),
+ high=Decimal("26"),
+ low=Decimal("20"),
+ close=Decimal("25"),
+ volume=Decimal("323"),
)
# tz-aware should normalize to UTC but represent the same instant
- d = getattr(q, 'date')
- assert str(d.tzinfo) == 'UTC'
- assert d.replace(microsecond=0) == dt.astimezone(timezone.utc).replace(microsecond=0)
+ d = q.date
+ assert str(d.tzinfo) == "UTC"
+ assert d.replace(microsecond=0) == dt.astimezone(timezone.utc).replace(
+ microsecond=0
+ )
diff --git a/tests/common/test_sma_roundtrip_dates.py b/tests/common/test_sma_roundtrip_dates.py
index a0f2d1d8..dbd3514c 100644
--- a/tests/common/test_sma_roundtrip_dates.py
+++ b/tests/common/test_sma_roundtrip_dates.py
@@ -1,4 +1,4 @@
-from datetime import datetime, timezone, timedelta
+from datetime import datetime, timedelta, timezone
from decimal import Decimal
import pytest
@@ -39,7 +39,9 @@ def _clone_with_date(quotes, transform):
def _mk_utc(d: datetime) -> datetime:
# Force tz-aware UTC at same Y-M-D h:m:s
- return datetime(d.year, d.month, d.day, d.hour, d.minute, d.second, tzinfo=timezone.utc)
+ return datetime(
+ d.year, d.month, d.day, d.hour, d.minute, d.second, tzinfo=timezone.utc
+ )
def _mk_offset(d: datetime) -> datetime:
@@ -59,7 +61,7 @@ def _mk_date_only(d: datetime) -> datetime:
@pytest.mark.parametrize(
- "variant, maker",
+ ("variant", "maker"),
[
("utc", _mk_utc),
("offset", _mk_offset),
diff --git a/tests/common/test_type_compatibility.py b/tests/common/test_type_compatibility.py
index 18da8b19..8f88eb58 100644
--- a/tests/common/test_type_compatibility.py
+++ b/tests/common/test_type_compatibility.py
@@ -1,15 +1,16 @@
-
from enum import Enum, IntEnum
-from stock_indicators._cslib import CsQuote, CsCandleProperties
+
+from stock_indicators._cslib import CsCandleProperties, CsQuote
from stock_indicators.indicators.common.candles import CandleProperties
from stock_indicators.indicators.common.enums import PivotPointType
from stock_indicators.indicators.common.quote import Quote
+
class TestTypeCompat:
def test_quote_based_class(self):
# Quote
assert issubclass(Quote, CsQuote)
-
+
# CandleProperties
assert issubclass(CandleProperties, Quote)
assert issubclass(CandleProperties, CsQuote)
diff --git a/tests/conftest.py b/tests/conftest.py
index c8fbc33d..b7a09bcb 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -18,7 +18,6 @@
import pytest
-from stock_indicators._cslib import clr
from stock_indicators.indicators.common import Quote # pre-initialized
from stock_indicators.logging_config import configure_logging
@@ -38,7 +37,7 @@ def setup_logging():
@pytest.fixture(autouse=True, scope="session")
def setup_clr_culture():
"""Configure CLR culture settings for all tests."""
- import clr
+ import clr # noqa: F401
from System.Globalization import CultureInfo
from System.Threading import Thread
@@ -63,7 +62,8 @@ def get_data_from_csv(filename):
quotes_dir = base_dir / "quotes"
if not base_dir.exists() or not quotes_dir.exists():
raise FileNotFoundError(
- "Test data not found. Please see README.md for test data setup instructions."
+ "Test data not found. Please see README.md "
+ "for test data setup instructions."
)
data_path = quotes_dir / f"{filename}.csv"
@@ -72,7 +72,7 @@ def get_data_from_csv(filename):
if not data_path.exists():
raise FileNotFoundError(f"Test data file not found: {filename}")
- with open(data_path, "r", newline="", encoding="utf-8") as csvfile:
+ with open(data_path, newline="", encoding="utf-8") as csvfile:
reader = csv.reader(csvfile)
data = list(reader)
return data[1:] # skips the first row, those are headers
@@ -89,11 +89,14 @@ def parse_decimal(value):
def parse_date(date_str):
"""Parse date value across many common formats.
+
Supported families:
- Date-only: YYYY-MM-DD, YYYYMMDD, DD-MM-YYYY, MM/DD/YYYY
- - Naive date+time: YYYY-MM-DDTHH:MM, YYYY-MM-DDTHH:MM:SS, YYYY-MM-DDTHH:MM:SS.sss[sss], YYYYMMDDTHHMMSS
- - With offset: ISO-8601 extended with offset (e.g., +00:00, -04:00, +05:30, with optional fractions);
- ISO basic with offset without colon: YYYYMMDDTHHMMSS+0000
+ - Naive date+time: YYYY-MM-DDTHH:MM, YYYY-MM-DDTHH:MM:SS,
+ YYYY-MM-DDTHH:MM:SS.sss[sss], YYYYMMDDTHHMMSS
+ - With offset: ISO-8601 extended with offset (e.g., +00:00, -04:00, +05:30,
+ with optional fractions); ISO basic with offset without colon:
+ YYYYMMDDTHHMMSS+0000
- Zulu: ...Z with optional fractional seconds
- RFC1123/HTTP-date: Fri, 22 Aug 2025 17:45:30 GMT
- IANA zone name appended after a space: YYYY-MM-DDTHH:MM:SS America/New_York
@@ -123,7 +126,8 @@ def parse_date(date_str):
try:
return dt.replace(tzinfo=ZoneInfo(zone))
except Exception:
- # Fallback if IANA zone isn't available on the system: treat as naive
+ # Fallback if IANA zone isn't available on the system:
+ # treat as naive
return dt
# ZoneInfo not available; treat as naive
return dt
@@ -144,7 +148,8 @@ def parse_date(date_str):
# ISO extended with Zulu or offset, including fractional seconds
if "T" in s and (s.endswith("Z") or re.search(r"[+-]\d{2}:?\d{2}$", s)):
s_norm = s.replace("Z", "+00:00")
- # If offset without colon at end (e.g., +0000), insert colon for fromisoformat
+ # If offset without colon at end (e.g., +0000), insert colon for
+ # fromisoformat
m_off = re.search(r"([+-])(\d{2})(\d{2})$", s_norm)
if m_off and ":" not in s_norm[-6:]:
s_norm = (
@@ -182,7 +187,8 @@ def parse_date(date_str):
# As a final attempt, try fromisoformat on whatever remains
return datetime.fromisoformat(s)
except Exception:
- # Last-resort fallback to keep tests running; individual tests will assert equality
+ # Last-resort fallback to keep tests running; individual tests will
+ # assert equality
return datetime.now()
diff --git a/tests/test_adl.py b/tests/test_adl.py
index 783d4551..cbc7d153 100644
--- a/tests/test_adl.py
+++ b/tests/test_adl.py
@@ -1,6 +1,8 @@
import pytest
+
from stock_indicators import indicators
+
class TestADL:
def test_standard(self, quotes):
results = indicators.get_adl(quotes)
@@ -11,15 +13,15 @@ def test_standard(self, quotes):
assert 502 == len(list(filter(lambda x: x.adl_sma is None, results)))
r1 = results[249]
- assert 0.7778 == round(float(r1.money_flow_multiplier), 4)
- assert 36433792.89 == round(float(r1.money_flow_volume), 2)
- assert 3266400865.74 == round(float(r1.adl), 2)
+ assert 0.7778 == round(float(r1.money_flow_multiplier), 4)
+ assert 36433792.89 == round(float(r1.money_flow_volume), 2)
+ assert 3266400865.74 == round(float(r1.adl), 2)
assert r1.adl_sma is None
r2 = results[501]
- assert 0.8052 == round(float(r2.money_flow_multiplier), 4)
- assert 118396116.25 == round(float(r2.money_flow_volume), 2)
- assert 3439986548.42 == round(float(r2.adl), 2)
+ assert 0.8052 == round(float(r2.money_flow_multiplier), 4)
+ assert 118396116.25 == round(float(r2.money_flow_volume), 2)
+ assert 3439986548.42 == round(float(r2.adl), 2)
assert r2.adl_sma is None
# def test_convert_to_quotes(self, quotes):
@@ -47,10 +49,10 @@ def test_with_sma(self, quotes):
assert 483 == len(list(filter(lambda x: x.adl_sma is not None, results)))
r = results[501]
- assert 0.8052 == round(float(r.money_flow_multiplier), 4)
- assert 118396116.25 == round(float(r.money_flow_volume), 2)
- assert 3439986548.42 == round(float(r.adl), 2)
- assert 3595352721.16 == round(float(r.adl_sma), 2)
+ assert 0.8052 == round(float(r.money_flow_multiplier), 4)
+ assert 118396116.25 == round(float(r.money_flow_volume), 2)
+ assert 3439986548.42 == round(float(r.adl), 2)
+ assert 3595352721.16 == round(float(r.adl_sma), 2)
def test_condense(self, quotes):
results = indicators.get_adl(quotes).condense()
@@ -58,12 +60,13 @@ def test_condense(self, quotes):
assert 502 == len(results)
r = results[-1]
- assert 0.8052 == round(float(r.money_flow_multiplier), 4)
- assert 118396116.25 == round(float(r.money_flow_volume), 2)
- assert 3439986548.42 == round(float(r.adl), 2)
+ assert 0.8052 == round(float(r.money_flow_multiplier), 4)
+ assert 118396116.25 == round(float(r.money_flow_volume), 2)
+ assert 3439986548.42 == round(float(r.adl), 2)
assert r.adl_sma is None
def test_exceptions(self, quotes):
from System import ArgumentOutOfRangeException
+
with pytest.raises(ArgumentOutOfRangeException):
indicators.get_adl(quotes, 0)
diff --git a/tests/test_adx.py b/tests/test_adx.py
index e5a24b68..040e1d3a 100644
--- a/tests/test_adx.py
+++ b/tests/test_adx.py
@@ -10,36 +10,63 @@ def test_standard(self, quotes):
# proper quantities
# should always be the same number of results as there is quotes
assert 502 == len(results)
+ assert 488 == len(list(filter(lambda x: x.dx is not None, results)))
assert 475 == len(list(filter(lambda x: x.adx is not None, results)))
- assert 462 == len(list(filter(lambda x: x.adxr is not None, results)))
+ assert 461 == len(list(filter(lambda x: x.adxr is not None, results)))
# sample values
+ r = results[13]
+ assert r.pdi is None
+ assert r.mdi is None
+ assert r.dx is None
+ assert r.adx is None
+
+ r = results[14]
+ assert 21.9669 == round(float(r.pdi), 4)
+ assert 18.5462 == round(float(r.mdi), 4)
+ assert 8.4433 == round(float(r.dx), 4)
+ assert r.adx is None
+
r = results[19]
assert 21.0361 == round(float(r.pdi), 4)
assert 25.0124 == round(float(r.mdi), 4)
+ assert 8.6351 == round(float(r.dx), 4)
assert r.adx is None
+ r = results[26]
+ assert r.adx is None
+
+ r = results[27]
+ assert 15.9459 == round(float(r.adx), 4)
+
r = results[29]
assert 37.9719 == round(float(r.pdi), 4)
assert 14.1658 == round(float(r.mdi), 4)
+ assert 45.6600 == round(float(r.dx), 4)
assert 19.7949 == round(float(r.adx), 4)
r = results[39]
assert r.adxr is None
r = results[40]
- assert 29.1062 == round(float(r.adxr), 4)
+ assert r.adxr is None
+
+ r = results[41]
+ assert r.adxr is not None
r = results[248]
assert 32.3167 == round(float(r.pdi), 4)
assert 18.2471 == round(float(r.mdi), 4)
+ assert 27.8255 == round(float(r.dx), 4)
assert 30.5903 == round(float(r.adx), 4)
- assert 29.1252 == round(float(r.adxr), 4)
+ assert r.adxr is not None
r = results[501]
assert 17.7565 == round(float(r.pdi), 4)
assert 31.1510 == round(float(r.mdi), 4)
+ assert 27.3873 == round(float(r.dx), 4)
assert 34.2987 == round(float(r.adx), 4)
+ assert r.adxr is not None
def test_bad_data(self, quotes_bad):
results = indicators.get_adx(quotes_bad, 20)
diff --git a/tests/test_basic_quote.py b/tests/test_basic_quote.py
index 5c4fde01..832ea008 100644
--- a/tests/test_basic_quote.py
+++ b/tests/test_basic_quote.py
@@ -4,11 +4,12 @@
from stock_indicators.indicators.common.enums import CandlePart
from stock_indicators.indicators.common.quote import Quote
+
class TestBasicQuote:
def test_standard(self, quotes):
o = indicators.get_basic_quote(quotes, CandlePart.OPEN)
h = indicators.get_basic_quote(quotes, CandlePart.HIGH)
- l = indicators.get_basic_quote(quotes, CandlePart.LOW)
+ low = indicators.get_basic_quote(quotes, CandlePart.LOW)
c = indicators.get_basic_quote(quotes, CandlePart.CLOSE)
v = indicators.get_basic_quote(quotes, CandlePart.VOLUME)
hl = indicators.get_basic_quote(quotes, CandlePart.HL2)
@@ -16,24 +17,24 @@ def test_standard(self, quotes):
oc = indicators.get_basic_quote(quotes, CandlePart.OC2)
ohl = indicators.get_basic_quote(quotes, CandlePart.OHL3)
ohlc = indicators.get_basic_quote(quotes, CandlePart.OHLC4)
-
+
assert 502 == len(c)
-
+
assert datetime(2018, 12, 31) == c[-1].date
-
- assert 244.92 == o[-1].value
- assert 245.54 == h[-1].value
- assert 242.87 == l[-1].value
- assert 245.28 == c[-1].value
- assert 147031456 == v[-1].value
- assert 244.205 == hl[-1].value
- assert 244.5633 == round(float(hlc[-1].value), 4)
- assert 245.1 == oc[-1].value
- assert 244.4433 == round(float(ohl[-1].value), 4)
- assert 244.6525 == ohlc[-1].value
-
+
+ assert 244.92 == o[-1].value
+ assert 245.54 == h[-1].value
+ assert 242.87 == low[-1].value
+ assert 245.28 == c[-1].value
+ assert 147031456 == v[-1].value
+ assert 244.205 == hl[-1].value
+ assert 244.5633 == round(float(hlc[-1].value), 4)
+ assert 245.1 == oc[-1].value
+ assert 244.4433 == round(float(ohl[-1].value), 4)
+ assert 244.6525 == ohlc[-1].value
+
def test_use(self, quotes):
results = Quote.use(quotes, CandlePart.CLOSE)
results = list(results)
-
+
assert 502 == len(results)
diff --git a/tests/test_connors_rsi.py b/tests/test_connors_rsi.py
index bcb759d7..d7fcb8c1 100644
--- a/tests/test_connors_rsi.py
+++ b/tests/test_connors_rsi.py
@@ -41,8 +41,6 @@ def test_removed(self, quotes):
streak_periods = 2
rank_periods = 100
- removed_periods = max(rsi_periods, max(streak_periods, rank_periods)) + 2
-
results = indicators.get_connors_rsi(
quotes, rsi_periods, streak_periods, rank_periods
)
diff --git a/tests/test_gator.py b/tests/test_gator.py
index 4ba3a309..c039af33 100644
--- a/tests/test_gator.py
+++ b/tests/test_gator.py
@@ -31,47 +31,46 @@ def test_standard(self, quotes):
assert r.upper is None
assert -0.0406 == round(float(r.lower), 4)
assert r.is_upper_expanding is None
- assert r.is_lower_expanding == False
+ assert not r.is_lower_expanding
r = results[19]
assert r.upper is None
assert -1.0018 == round(float(r.lower), 4)
assert r.is_upper_expanding is None
- assert r.is_lower_expanding == True
+ assert r.is_lower_expanding
r = results[20]
assert 0.4004 == round(float(r.upper), 4)
assert -1.0130 == round(float(r.lower), 4)
assert r.is_upper_expanding is None
- assert r.is_lower_expanding == True
+ assert r.is_lower_expanding
r = results[21]
assert 0.7298 == round(float(r.upper), 4)
assert -0.6072 == round(float(r.lower), 4)
- assert r.is_upper_expanding == True
- assert r.is_lower_expanding == False
+ assert r.is_upper_expanding
+ assert not r.is_lower_expanding
r = results[99]
assert 0.5159 == round(float(r.upper), 4)
assert -0.2320 == round(float(r.lower), 4)
- assert r.is_upper_expanding == False
- assert r.is_lower_expanding == True
+ assert not r.is_upper_expanding
+ assert r.is_lower_expanding
r = results[249]
assert 3.1317 == round(float(r.upper), 4)
assert -1.8058 == round(float(r.lower), 4)
- assert r.is_upper_expanding == True
- assert r.is_lower_expanding == False
+ assert r.is_upper_expanding
+ assert not r.is_lower_expanding
r = results[501]
assert 7.4538 == round(float(r.upper), 4)
assert -9.2399 == round(float(r.lower), 4)
- assert r.is_upper_expanding == True
- assert r.is_lower_expanding == True
+ assert r.is_upper_expanding
+ assert r.is_lower_expanding
def test_gator_with_alligator(self, quotes):
alligator_results = indicators.get_alligator(quotes)
- alligator_results.done()
results = indicators.get_gator(alligator_results)
assert 502 == len(results)
@@ -95,8 +94,8 @@ def test_removed(self, quotes):
last = results.pop()
assert 7.4538 == round(float(last.upper), 4)
assert -9.2399 == round(float(last.lower), 4)
- assert last.is_upper_expanding == True
- assert last.is_lower_expanding == True
+ assert last.is_upper_expanding
+ assert last.is_lower_expanding
def test_condense(self, quotes):
results = indicators.get_gator(quotes).condense()
@@ -106,5 +105,5 @@ def test_condense(self, quotes):
last = results.pop()
assert 7.4538 == round(float(last.upper), 4)
assert -9.2399 == round(float(last.lower), 4)
- assert last.is_upper_expanding == True
- assert last.is_lower_expanding == True
+ assert last.is_upper_expanding
+ assert last.is_lower_expanding
diff --git a/tests/test_hurst.py b/tests/test_hurst.py
index 8b653f4f..47f601de 100644
--- a/tests/test_hurst.py
+++ b/tests/test_hurst.py
@@ -14,7 +14,9 @@ def test_standard_long(self, quotes_longest):
assert 0.483563 == round(float(r.hurst_exponent), 6)
# def test_to_quotes(self, quotes_longest):
- # new_quotes = indicators.get_hurst(quotes_longest, len(quotes_longest) - 1).to_quotes()
+ # new_quotes = indicators.get_hurst(
+ # quotes_longest, len(quotes_longest) - 1
+ # ).to_quotes()
# assert 1 == len(new_quotes)
diff --git a/tests/test_parabolic_sar.py b/tests/test_parabolic_sar.py
index fb248f19..5af9acb2 100644
--- a/tests/test_parabolic_sar.py
+++ b/tests/test_parabolic_sar.py
@@ -12,19 +12,19 @@ def test_standard(self, quotes):
r = results[14]
assert 212.8300 == round(float(r.sar), 4)
- assert True == r.is_reversal
+ assert r.is_reversal
r = results[16]
assert 212.9924 == round(float(r.sar), 4)
- assert False == r.is_reversal
+ assert not r.is_reversal
r = results[94]
assert 228.3600 == round(float(r.sar), 4)
- assert False == r.is_reversal
+ assert not r.is_reversal
r = results[501]
assert 229.7662 == round(float(r.sar), 4)
- assert False == r.is_reversal
+ assert not r.is_reversal
def test_extended(self, quotes):
accleration_step = 0.02
@@ -40,23 +40,23 @@ def test_extended(self, quotes):
r = results[14]
assert 212.8300 == round(float(r.sar), 4)
- assert True == r.is_reversal
+ assert r.is_reversal
r = results[16]
assert 212.9518 == round(float(r.sar), 4)
- assert False == r.is_reversal
+ assert not r.is_reversal
r = results[94]
assert 228.3600 == round(float(r.sar), 4)
- assert False == r.is_reversal
+ assert not r.is_reversal
r = results[486]
assert 273.4148 == round(float(r.sar), 4)
- assert False == r.is_reversal
+ assert not r.is_reversal
r = results[501]
assert 246.73 == round(float(r.sar), 4)
- assert False == r.is_reversal
+ assert not r.is_reversal
def test_bad_data(self, quotes_bad):
r = indicators.get_parabolic_sar(quotes_bad)
@@ -71,7 +71,7 @@ def test_removed(self, quotes):
last = results.pop()
assert 229.7662 == round(float(last.sar), 4)
- assert False == last.is_reversal
+ assert not last.is_reversal
def test_condense(self, quotes):
results = indicators.get_parabolic_sar(quotes, 0.02, 0.2).condense()
@@ -80,7 +80,7 @@ def test_condense(self, quotes):
last = results.pop()
assert 229.7662 == round(float(last.sar), 4)
- assert False == last.is_reversal
+ assert not last.is_reversal
def test_exceptions(self, quotes):
from System import ArgumentOutOfRangeException
diff --git a/tests/test_renko.py b/tests/test_renko.py
index 72c20f81..56e389ba 100644
--- a/tests/test_renko.py
+++ b/tests/test_renko.py
@@ -18,7 +18,7 @@ def test_standard_close(self, quotes):
assert 212.53 == float(round(r.low, 2))
assert 215.5 == float(round(r.close, 1))
assert 1180981564 == float(round(r.volume, 0))
- assert r.is_up == True
+ assert r.is_up
r = results[5]
assert 225.5 == float(round(r.open, 1))
@@ -26,7 +26,7 @@ def test_standard_close(self, quotes):
assert 219.77 == float(round(r.low, 2))
assert 228 == float(round(r.close, 0))
assert 4192959240 == float(round(r.volume, 0))
- assert r.is_up == True
+ assert r.is_up
r = results.pop()
assert 240.5 == float(round(r.open, 1))
@@ -34,7 +34,7 @@ def test_standard_close(self, quotes):
assert 234.52 == float(round(r.low, 2))
assert 243 == float(round(r.close, 0))
assert 189794032 == float(round(r.volume, 0))
- assert r.is_up == True
+ assert r.is_up
def test_standard_high_low(self, quotes):
results = indicators.get_renko(quotes, 2.5, EndType.HIGH_LOW)
@@ -47,7 +47,7 @@ def test_standard_high_low(self, quotes):
assert 212.53 == float(round(r.low, 2))
assert 215.5 == float(round(r.close, 1))
assert 1180981564 == float(round(r.volume, 0))
- assert r.is_up == True
+ assert r.is_up
r = results[25]
assert 270.5 == float(round(r.open, 1))
@@ -55,7 +55,7 @@ def test_standard_high_low(self, quotes):
assert 271.96 == float(round(r.low, 2))
assert 273 == float(round(r.close, 0))
assert 100801672 == float(round(r.volume, 0))
- assert r.is_up == True
+ assert r.is_up
r = results.pop()
assert 243 == float(round(r.open, 0))
@@ -63,7 +63,7 @@ def test_standard_high_low(self, quotes):
assert 241.87 == float(round(r.low, 2))
assert 245.5 == float(round(r.close, 1))
assert 51999637 == float(round(r.volume, 0))
- assert r.is_up == True
+ assert r.is_up
def test_renko_atr(self, quotes):
results = indicators.get_renko_atr(quotes, 14, EndType.CLOSE)
@@ -76,7 +76,7 @@ def test_renko_atr(self, quotes):
assert 212.53 == float(round(r.low, 2))
assert 218.9497 == float(round(r.close, 4))
assert 2090292272 == float(round(r.volume, 0))
- assert r.is_up == True
+ assert r.is_up
r = results.pop()
assert 237.3990 == float(round(r.open, 4))
@@ -84,7 +84,7 @@ def test_renko_atr(self, quotes):
assert 229.42 == float(round(r.low, 2))
assert 243.5487 == float(round(r.close, 4))
assert 715446448 == float(round(r.volume, 0))
- assert r.is_up == True
+ assert r.is_up
def test_bad_data(self, quotes_bad):
r = indicators.get_renko(quotes_bad, 100)
diff --git a/tests/test_sma_analysis.py b/tests/test_sma_analysis.py
index b7a49a4f..a5a0c5f3 100644
--- a/tests/test_sma_analysis.py
+++ b/tests/test_sma_analysis.py
@@ -1,7 +1,6 @@
import pytest
from stock_indicators import indicators
-from stock_indicators._cslib import CsDecimal
class TestSMAExtended:
@@ -10,10 +9,10 @@ def test_result_types(self, quotes):
# Sample value.
r = results[501]
- assert float == type(r._csdata.Sma)
- assert float == type(r._csdata.Mad)
- assert float == type(r._csdata.Mse)
- assert float == type(r._csdata.Mape)
+ assert isinstance(r._csdata.Sma, float)
+ assert isinstance(r._csdata.Mad, float)
+ assert isinstance(r._csdata.Mse, float)
+ assert isinstance(r._csdata.Mape, float)
def test_extended(self, quotes):
results = indicators.get_sma_analysis(quotes, 20)
diff --git a/tests/test_starc_bands.py b/tests/test_starc_bands.py
index e4f3bf56..981db6d7 100644
--- a/tests/test_starc_bands.py
+++ b/tests/test_starc_bands.py
@@ -8,7 +8,6 @@ def test_standard(self, quotes):
sma_periods = 20
multiplier = 2
atr_periods = 14
- lookback_periods = max(sma_periods, atr_periods)
results = indicators.get_starc_bands(
quotes, sma_periods, multiplier, atr_periods
diff --git a/tests/test_stdev.py b/tests/test_stdev.py
index 4e622236..87836176 100644
--- a/tests/test_stdev.py
+++ b/tests/test_stdev.py
@@ -1,6 +1,8 @@
import pytest
+
from stock_indicators import indicators
+
class TestStdev:
def test_standard(self, quotes):
results = indicators.get_stdev(quotes, 10)
@@ -8,7 +10,7 @@ def test_standard(self, quotes):
assert 502 == len(results)
assert 493 == len(list(filter(lambda x: x.stdev is not None, results)))
assert 493 == len(list(filter(lambda x: x.z_score is not None, results)))
- assert 0 == len(list(filter(lambda x: x.stdev_sma is not None, results)))
+ assert 0 == len(list(filter(lambda x: x.stdev_sma is not None, results)))
r = results[8]
assert r.stdev is None
@@ -17,19 +19,19 @@ def test_standard(self, quotes):
assert r.stdev_sma is None
r = results[9]
- assert 0.5020 == round(float(r.stdev), 4)
- assert 214.0140 == round(float(r.mean), 4)
+ assert 0.5020 == round(float(r.stdev), 4)
+ assert 214.0140 == round(float(r.mean), 4)
assert -0.525917 == round(float(r.z_score), 6)
assert r.stdev_sma is None
r = results[249]
- assert 0.9827 == round(float(r.stdev), 4)
+ assert 0.9827 == round(float(r.stdev), 4)
assert 257.2200 == round(float(r.mean), 4)
assert 0.783563 == round(float(r.z_score), 6)
assert r.stdev_sma is None
r = results[501]
- assert 5.4738 == round(float(r.stdev), 4)
+ assert 5.4738 == round(float(r.stdev), 4)
assert 242.4100 == round(float(r.mean), 4)
assert 0.524312 == round(float(r.z_score), 6)
assert r.stdev_sma is None
@@ -43,14 +45,14 @@ def test_stdev_with_sma(self, quotes):
assert 489 == len(list(filter(lambda x: x.stdev_sma is not None, results)))
r = results[19]
- assert 1.1642 == round(float(r.stdev), 4)
+ assert 1.1642 == round(float(r.stdev), 4)
assert -0.065282 == round(float(r.z_score), 6)
- assert 1.1422 == round(float(r.stdev_sma), 4)
+ assert 1.1422 == round(float(r.stdev_sma), 4)
r = results[501]
- assert 5.4738 == round(float(r.stdev), 4)
+ assert 5.4738 == round(float(r.stdev), 4)
assert 0.524312 == round(float(r.z_score), 6)
- assert 7.6886 == round(float(r.stdev_sma), 4)
+ assert 7.6886 == round(float(r.stdev_sma), 4)
def test_bad_data(self, quotes_bad):
r = indicators.get_stdev(quotes_bad, 15, 3)
@@ -73,7 +75,7 @@ def test_removed(self, quotes):
assert 502 - 9 == len(results)
last = results.pop()
- assert 5.4738 == round(float(last.stdev), 4)
+ assert 5.4738 == round(float(last.stdev), 4)
assert 242.4100 == round(float(last.mean), 4)
assert 0.524312 == round(float(last.z_score), 6)
assert last.stdev_sma is None
@@ -84,15 +86,16 @@ def test_condense(self, quotes):
assert 493 == len(results)
last = results.pop()
- assert 5.4738 == round(float(last.stdev), 4)
+ assert 5.4738 == round(float(last.stdev), 4)
assert 242.4100 == round(float(last.mean), 4)
assert 0.524312 == round(float(last.z_score), 6)
assert last.stdev_sma is None
def test_exceptions(self, quotes):
from System import ArgumentOutOfRangeException
+
with pytest.raises(ArgumentOutOfRangeException):
indicators.get_stdev(quotes, 1)
with pytest.raises(ArgumentOutOfRangeException):
- indicators.get_stdev(quotes, 14,0)
+ indicators.get_stdev(quotes, 14, 0)
diff --git a/tests/test_stoch.py b/tests/test_stoch.py
index e53cff51..c665997b 100644
--- a/tests/test_stoch.py
+++ b/tests/test_stoch.py
@@ -125,9 +125,6 @@ def test_boundary(self, quotes):
assert 0 == len(
list(filter(lambda x: x.d is not None and (x.d < 0 or x.d > 100), results))
)
- assert 0 == len(
- list(filter(lambda x: x.j is not None and (x.d < 0 or x.d > 100), results))
- )
def test_removed(self, quotes):
results = indicators.get_stoch(quotes, 14, 3, 3).remove_warmup_periods()
diff --git a/tests/test_trix.py b/tests/test_trix.py
index 12392e80..1ee5a0dd 100644
--- a/tests/test_trix.py
+++ b/tests/test_trix.py
@@ -1,6 +1,8 @@
import pytest
+
from stock_indicators import indicators
+
class TestTRIX:
def test_standard(self, quotes):
results = indicators.get_trix(quotes, 20, 5)
@@ -21,7 +23,7 @@ def test_standard(self, quotes):
assert 0.119769 == round(float(r.signal), 6)
r = results[501]
- assert 263.3216 == round(float(r.ema3), 4)
+ assert 263.3216 == round(float(r.ema3), 4)
assert -0.230742 == round(float(r.trix), 6)
assert -0.204536 == round(float(r.signal), 6)
@@ -35,7 +37,7 @@ def test_removed(self, quotes):
assert 502 - ((3 * 20) + 100) == len(results)
last = results.pop()
- assert 263.3216 == round(float(last.ema3), 4)
+ assert 263.3216 == round(float(last.ema3), 4)
assert -0.230742 == round(float(last.trix), 6)
assert -0.204536 == round(float(last.signal), 6)
@@ -45,11 +47,12 @@ def test_condense(self, quotes):
assert 482 == len(results)
last = results.pop()
- assert 263.3216 == round(float(last.ema3), 4)
+ assert 263.3216 == round(float(last.ema3), 4)
assert -0.230742 == round(float(last.trix), 6)
assert -0.204536 == round(float(last.signal), 6)
def test_exceptions(self, quotes):
from System import ArgumentOutOfRangeException
+
with pytest.raises(ArgumentOutOfRangeException):
indicators.get_trix(quotes, 0)
diff --git a/tests/test_tsi.py b/tests/test_tsi.py
index 1012e80e..e43ad45d 100644
--- a/tests/test_tsi.py
+++ b/tests/test_tsi.py
@@ -1,6 +1,8 @@
import pytest
+
from stock_indicators import indicators
+
class TestTSI:
def test_standard(self, quotes):
results = indicators.get_tsi(quotes, 25, 13, 7)
@@ -68,6 +70,7 @@ def test_condense(self, quotes):
def test_exceptions(self, quotes):
from System import ArgumentOutOfRangeException
+
with pytest.raises(ArgumentOutOfRangeException):
indicators.get_tsi(quotes, 0)
diff --git a/tests/test_ulcer_index.py b/tests/test_ulcer_index.py
index 7747e629..770134ac 100644
--- a/tests/test_ulcer_index.py
+++ b/tests/test_ulcer_index.py
@@ -1,6 +1,8 @@
import pytest
+
from stock_indicators import indicators
+
class TestUlcerIndex:
def test_standard(self, quotes):
results = indicators.get_ulcer_index(quotes, 14)
@@ -40,5 +42,6 @@ def test_condense(self, quotes):
def test_exceptions(self, quotes):
from System import ArgumentOutOfRangeException
+
with pytest.raises(ArgumentOutOfRangeException):
indicators.get_ulcer_index(quotes, 0)
diff --git a/tests/test_vwap.py b/tests/test_vwap.py
index 32eeb694..2de05ac0 100644
--- a/tests/test_vwap.py
+++ b/tests/test_vwap.py
@@ -6,7 +6,6 @@
class TestVWAP:
-
def test_standard(self, quotes_intraday):
quotes_intraday.sort(key=lambda x: x.date)
results = indicators.get_vwap(quotes_intraday[:391])
diff --git a/tests/utiltest.py b/tests/utiltest.py
index cab72173..41f088c6 100644
--- a/tests/utiltest.py
+++ b/tests/utiltest.py
@@ -1,20 +1,25 @@
-import os
import json
+import os
from datetime import datetime
from stock_indicators.indicators.common.quote import Quote
+
def load_quotes_from_json(json_path):
base_dir = os.path.dirname(__file__)
data_path = os.path.join(base_dir, json_path)
quotes = []
- with open(data_path, "r", encoding="utf-8") as st_json:
+ with open(data_path, encoding="utf-8") as st_json:
for j in json.load(st_json):
- quotes.append(Quote(datetime.fromisoformat(j["Date"]),
+ quotes.append(
+ Quote(
+ datetime.fromisoformat(j["Date"]),
j["Open"],
j["High"],
j["Low"],
j["Close"],
- j["Volume"]))
+ j["Volume"],
+ )
+ )
return quotes
diff --git a/typings/Skender/Stock/Indicators/__init__.pyi b/typings/Skender/Stock/Indicators/__init__.pyi
new file mode 100644
index 00000000..5a21d12b
--- /dev/null
+++ b/typings/Skender/Stock/Indicators/__init__.pyi
@@ -0,0 +1,17 @@
+from typing import Any
+
+BetaType: Any
+CandlePart: Any
+CandleProperties: Any
+ChandelierType: Any
+EndType: Any
+Indicator: Any
+Match: Any
+MaType: Any
+PeriodSize: Any
+PivotPointType: Any
+PivotTrend: Any
+Quote: Any
+QuoteUtility: Any
+ResultBase: Any
+ResultUtility: Any
diff --git a/typings/System/Collections/Generic/__init__.pyi b/typings/System/Collections/Generic/__init__.pyi
new file mode 100644
index 00000000..896f37b9
--- /dev/null
+++ b/typings/System/Collections/Generic/__init__.pyi
@@ -0,0 +1,4 @@
+from typing import Any
+
+IEnumerable: Any
+List: Any
diff --git a/typings/System/Globalization/__init__.pyi b/typings/System/Globalization/__init__.pyi
new file mode 100644
index 00000000..03519532
--- /dev/null
+++ b/typings/System/Globalization/__init__.pyi
@@ -0,0 +1,4 @@
+from typing import Any
+
+CultureInfo: Any
+NumberStyles: Any
diff --git a/typings/System/Threading/__init__.pyi b/typings/System/Threading/__init__.pyi
new file mode 100644
index 00000000..66e340af
--- /dev/null
+++ b/typings/System/Threading/__init__.pyi
@@ -0,0 +1,3 @@
+from typing import Any
+
+Thread: Any
diff --git a/typings/System/__init__.pyi b/typings/System/__init__.pyi
new file mode 100644
index 00000000..baacb27f
--- /dev/null
+++ b/typings/System/__init__.pyi
@@ -0,0 +1,8 @@
+from typing import Any
+
+ArgumentOutOfRangeException: Any
+DateTime: Any
+Decimal: Any
+Enum: Any
+IO: Any
+AppDomain: Any
diff --git a/typings/pythonnet/__init__.pyi b/typings/pythonnet/__init__.pyi
new file mode 100644
index 00000000..4b822079
--- /dev/null
+++ b/typings/pythonnet/__init__.pyi
@@ -0,0 +1,3 @@
+from typing import Any
+
+def load(runtime: str | None = ...) -> Any: ...
diff --git a/typings/stock_indicators/_cslib/__init__.pyi b/typings/stock_indicators/_cslib/__init__.pyi
new file mode 100644
index 00000000..116d292c
--- /dev/null
+++ b/typings/stock_indicators/_cslib/__init__.pyi
@@ -0,0 +1,25 @@
+from typing import Any
+
+clr: Any
+CsIndicator: Any
+CsResultUtility: Any
+CsQuote: Any
+CsQuoteUtility: Any
+CsResultBase: Any
+CsBetaType: Any
+CsCandlePart: Any
+CsCandleProperties: Any
+CsChandelierType: Any
+CsEndType: Any
+CsMatch: Any
+CsMaType: Any
+CsPeriodSize: Any
+CsPivotPointType: Any
+CsPivotTrend: Any
+CsDateTime: Any
+CsDecimal: Any
+CsEnum: Any
+CsIEnumerable: Any
+CsList: Any
+CsCultureInfo: Any
+CsNumberStyles: Any