Repository: gregyjames/Panther
Branch: main
Commit: 9050091feb27
Files: 10
Total size: 16.6 KB
Directory structure:
gitextract_gksj4ub1/
├── .github/
│ └── workflows/
│ ├── CI.yml
│ └── rust.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── pyproject.toml
├── speed_tests/
│ ├── ema.py
│ └── sma.py
└── src/
└── lib.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/CI.yml
================================================
name: CI
on:
push:
branches:
- main
tags:
- "*"
pull_request:
jobs:
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
architecture: x64
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
profile: minimal
default: true
- name: Build wheels - x86_64
uses: messense/maturin-action@v1
with:
target: x86_64
args: --release --out dist
- name: Install built wheel - x86_64
run: |
pip install numpy
pip install ZenithTA --no-deps --no-index --find-links dist --force-reinstall
python -c 'import ZenithTA'
- name: Build wheels - universal2
uses: messense/maturin-action@v1
with:
args: --release --universal2 --out dist --no-sdist
- name: Install built wheel - universal2
run: |
pip install ZenithTA --no-deps --no-index --find-links dist --force-reinstall
python -c 'import ZenithTA'
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
windows:
runs-on: windows-latest
strategy:
matrix:
target: [x86]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
architecture: ${{ matrix.target }}
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
profile: minimal
default: true
- name: Build wheels
uses: messense/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist --no-sdist
- name: Install built wheel
run: |
pip install numpy
pip install ZenithTA --no-deps --no-index --find-links dist --force-reinstall
python -c 'import ZenithTA'
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
linux:
runs-on: ubuntu-latest
strategy:
matrix:
target: [x86_64, i686]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
architecture: x64
- name: Build wheels
uses: messense/maturin-action@v1
with:
rust-toolchain: nightly
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist --no-sdist
- name: Install built wheel
if: matrix.target == 'x86_64'
run: |
pip install numpy
pip install ZenithTA --no-deps --no-index --find-links dist --force-reinstall
python -c 'import ZenithTA'
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
linux-cross:
runs-on: ubuntu-latest
strategy:
matrix:
target: [aarch64, armv7, s390x, ppc64le]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Build wheels
uses: messense/maturin-action@v1
with:
rust-toolchain: nightly
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist --no-sdist
- uses: uraimo/run-on-arch-action@v2.0.5
name: Install built wheel
if: matrix.target == 'aarch64'
with:
arch: ${{ matrix.target }}
distro: ubuntu20.04
githubToken: ${{ github.token }}
install: |
apt-get update
apt-get install -y --no-install-recommends python3 python3-pip
pip3 install -U pip numpy
run: |
pip3 install ZenithTA --no-deps --no-index --find-links dist/ --force-reinstall
python3 -c 'import ZenithTA'
- name: Upload wheels
uses: actions/upload-artifact@v2
with:
name: wheels
path: dist
release:
name: Release
runs-on: ubuntu-latest
if: "startsWith(github.ref, 'refs/tags/')"
needs: [ macos, windows, linux, linux-cross ]
steps:
- uses: actions/download-artifact@v3
with:
name: wheels
- name: Publish to PyPi
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
uses: PyO3/maturin-action@v1
with:
command: upload
args: --skip-existing *
================================================
FILE: .github/workflows/rust.yml
================================================
name: Rust
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cargo Cache
uses: actions/cache@v1
with:
path: ~/.cargo
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }}
${{ runner.os }}-cargo
- name: Cargo Target Cache
uses: actions/cache@v1
with:
path: target
key: ${{ runner.os }}-cargo-target-${{ hashFiles('Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-target-${{ hashFiles('Cargo.toml') }}
${{ runner.os }}-cargo-target
- name: Build
run: cargo build --release --verbose
- name: Run tests
run: cargo test --verbose
================================================
FILE: .gitignore
================================================
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# Added by cargo
#
# already existing elements were commented out
/target
#Cargo.lock
# Add for maturin
dist/
================================================
FILE: Cargo.toml
================================================
[package]
name = "ZenithTA"
version = "1.0.0"
edition = "2021"
[lib]
name = "ZenithTA"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.15.1", features = ["abi3-py36", "extension-module"] }
numpy = "0.15"
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2022 Greg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
[](https://github.com/gregyjames/ZenithTA/actions/workflows/rust.yml)
[](https://pypi.org/project/zenithta/)
[](https://github.com/gregyjames/ZenithTA/blob/main/LICENSE)
[](https://github.com/gregyjames/ZenithTA/)
# ZenithTA
#### Formerly Panther
A efficient, high-performance python technical analysis library written in Rust using PyO3 and rust-numpy.
## Indicators
- ATR
- CMF
- SMA
- EMA
- RSI
- MACD
- ROC
## How to install
`pip3 install zenithta`
## How to build (Windows)
- Run `cargo build --release` from the main directory.
- Get the generated dll from the target/release directory.
- Rename extension from .dll to .pyd.
- Place .pyd file in the same folder as script.
- Put `from panther import *` in python script.
## Speed
On average, I found the Panther calculations of these indicators to be about 9x or 900% faster than the industry standard way of calculating these indicators using Pandas. Don't believe me? Install the library and run the tests in the speed_tests directory to see it for yourself :)
## License
MIT License
Copyright (c) 2022 Greg James
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: pyproject.toml
================================================
[build-system]
requires = ["maturin>=0.12,<0.13"]
build-backend = "maturin"
[project]
name = "ZenithTA"
dependencies = [
"numpy"
]
================================================
FILE: speed_tests/ema.py
================================================
import pandas_datareader as pdr
import pandas as pd
from ZenithTA import *
from timeit import default_timer as timer
from datetime import timedelta
data = pdr.get_data_yahoo('NVDA')
print("Timing ZenithTA:")
start = timer()
a = data['Close'].tolist()
j = ema(a,4,2.0)
end = timer()
print(j[0:10:1])
print(timedelta(seconds=end-start))
print("Timing Pandas:")
start = timer()
data['4dayEWM'] = data['Close'].ewm(span=4, adjust=False).mean()
b = data['4dayEWM'].tolist()
print(b[0:10:1])
end = timer()
print(timedelta(seconds=end-start))
================================================
FILE: speed_tests/sma.py
================================================
import pandas_datareader as pdr
import pandas as pd
from ZenithTA import *
from timeit import default_timer as timer
from datetime import timedelta
data = pdr.get_data_yahoo('NVDA')
print("Timing ZenithTA:")
a = data['Close'].tolist()
start = timer()
j = sma(a,5)
end = timer()
print(timedelta(seconds=end-start))
print("Timing Pandas:")
start = timer()
data['SMA(5)'] = data.Close.rolling(5).mean()
end = timer()
b = data['SMA(5)'].tolist()
print(timedelta(seconds=end-start))
================================================
FILE: src/lib.rs
================================================
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
use numpy::ndarray::prelude::*;
//Helper functions for indicators
fn sma_helper(price_ndarray: &Array1<f32>, period: usize) -> Array1<f32>{
let length = price_ndarray.len() - period +1;
let mut result = Array1::<f32>::zeros(length);
for i in 0..length {
let slice = price_ndarray.slice(s![i..i+period]);
result[i] = slice.sum()/(period as f32);
}
result
}
fn ema_helper(price_ndarray: &Array1<f32>, period: usize) -> Array1<f32>{
let length = price_ndarray.len() - period +1;
let mut result = Array1::<f32>::zeros(length);
result[0] = price_ndarray.slice(s![0..period]).sum();
for i in 1..length{
result[i] = result[i-1]+(price_ndarray[i+period-1]-result[i-1])*(2.0/((period as f32)+1.0));
}
result
}
//Indicator Python function wrappers
#[pyfunction]
fn sma(price: Vec<f32>, period: usize) -> PyResult<Vec<f32>> {
let price_ndarray = Array::from_vec(price);
let length = price_ndarray.len() - period +1;
let mut result = Array1::<f32>::zeros(length);
for i in 0..length {
let slice = price_ndarray.slice(s![i..i+period]);
result[i] = slice.sum()/(period as f32);
}
Ok(Array::to_vec(&result))
}
#[pyfunction]
fn ema(price: Vec<f32>, period: usize, smoothing: f32) -> PyResult<Vec<f32>> {
let price_ndarray = Array::from_vec(price);
let length = price_ndarray.len();
let mut result = Array1::<f32>::zeros(length);
let weight = smoothing / (period + 1) as f32;
result[0] = price_ndarray[0];
for i in 1..length {
result[i] = (price_ndarray[i]*weight) + (result[i-1] * (1.0-weight));
}
Ok(Array::to_vec(&result))
}
#[pyfunction]
fn rsi(price: Vec<f32>, period: usize) -> PyResult<Vec<f32>>{
let price_ndarray = Array::from_vec(price);
let mut change = Array1::<f32>::zeros(price_ndarray.len());
let mut gain = Array1::<f32>::zeros(price_ndarray.len());
let mut loss = Array1::<f32>::zeros(price_ndarray.len());
let mut ag = Array1::<f32>::zeros(price_ndarray.len());
let mut al = Array1::<f32>::zeros(price_ndarray.len());
let mut result = Array1::<f32>::zeros(price_ndarray.len());
for i in 1..price_ndarray.len(){
change[i] = price_ndarray[i] - price_ndarray[i-1];
if change[i] == 0.0{
gain[i] = 0.0;
loss[i] = 0.0;
} else if change[i]<0.0{
gain[i] = 0.0;
loss[i] = (change[i]).abs();
}else{
gain[i] = change[i];
loss[i] = 0.0;
}
}
ag[period] = gain.slice(s![1..period+1]).sum()/(period as f32);
al[period] = loss.slice(s![1..period+1]).sum()/(period as f32);
for i in period+1..price_ndarray.len(){
ag[i] = (ag[i-1]*(period as f32-1.0)+gain[i])/period as f32;
al[i] = (al[i-1]*(period as f32-1.0)+loss[i])/period as f32;
}
for i in period+1..price_ndarray.len(){
result[i] = 100.0-(100.0/(1.0+ag[i]/al[i]))
}
//result.slice(s![period..]).to_owned()
Ok(Array::to_vec(&(result.slice(s![period..]).to_owned())))
}
#[pyfunction]
fn macd(price: Vec<f32>, period_fast: usize, period_slow: usize, period_signal: usize) -> PyResult<(Vec<f32>,Vec<f32>)>{
let price_ndarray = Array::from_vec(price);
let line = ema_helper(&price_ndarray, period_fast).slice(s![period_slow-period_fast..]).to_owned() - ema_helper(&price_ndarray,period_slow);
let signal = ema_helper(&price_ndarray, period_signal);
Ok((Array::to_vec(&line), Array::to_vec(&signal)))
}
#[pyfunction]
fn roc(price: Vec<f32>, period: usize) -> PyResult<Vec<f32>> {
let price_ndarray = Array::from_vec(price);
let length = price_ndarray.len() - period;
let mut result = Array1::<f32>::zeros(length);
for i in period..price_ndarray.len() {
result[i-period] = ((price_ndarray[i]-price_ndarray[i-period])/price_ndarray[i-period])*100.0;
}
Ok(Array::to_vec(&result))
}
#[pyfunction]
fn atr(high: Vec<f32>, low: Vec<f32>, close: Vec<f32>, period: usize) -> PyResult<Vec<f32>> {
let length = high.len();
let high_ndarray = Array::from_vec(high);
let low_ndarray = Array::from_vec(low);
let close_ndarray = Array::from_vec(close);
let mut tr = Array1::<f32>::zeros(length);
let mut result = Array1::<f32>::zeros(length - period + 1);
tr[0] = high_ndarray[0]-low_ndarray[0];
for i in 1..high_ndarray.len() {
let hl = high_ndarray[i] - low_ndarray[i];
let hpc = (high_ndarray[i] - close_ndarray[i-1]).abs();
let lpc = (low_ndarray[i] - close_ndarray[i-1]).abs();
tr[i] = f32::max(f32::max(hl, hpc), lpc);
}
result[0] = tr.slice(s![0..period]).sum()/period as f32;
for i in 1.. length -period+1 {
result[i] = (result[i-1]*(period as f32-1.0)+tr[i+period-1])/period as f32;
}
Ok(Array::to_vec(&result))
}
#[pyfunction]
fn cmf(high: Vec<f32>, low: Vec<f32>, close: Vec<f32>, volume: Vec<f32>, period: usize) -> PyResult<Vec<f32>> {
let length = high.len();
let high_ndarray = Array::from_vec(high);
let low_ndarray = Array::from_vec(low);
let close_ndarray = Array::from_vec(close);
let volume_ndarray = Array::from_vec(volume);
let mut result = Array1::<f32>::zeros(length - period + 1);
let a = (&close_ndarray)-(&low_ndarray);
let b = (&high_ndarray)-(&close_ndarray);
let c = (&high_ndarray)-(&low_ndarray);
let mfv = ((a-b)/c)*period as f32;
for i in 0..length-period+1 {
result[i] = mfv.slice(s![i..i+period]).sum()/ volume_ndarray.slice(s![i..i+period]).sum();
}
Ok(Array::to_vec(&result))
}
#[pymodule]
fn ZenithTA(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sma, m)?)?;
m.add_function(wrap_pyfunction!(cmf, m)?)?;
m.add_function(wrap_pyfunction!(atr, m)?)?;
m.add_function(wrap_pyfunction!(ema, m)?)?;
m.add_function(wrap_pyfunction!(rsi, m)?)?;
m.add_function(wrap_pyfunction!(roc, m)?)?;
m.add_function(wrap_pyfunction!(macd, m)?)?;
Ok(())
}
gitextract_gksj4ub1/
├── .github/
│ └── workflows/
│ ├── CI.yml
│ └── rust.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── pyproject.toml
├── speed_tests/
│ ├── ema.py
│ └── sma.py
└── src/
└── lib.rs
SYMBOL INDEX (10 symbols across 1 files)
FILE: src/lib.rs
function sma_helper (line 6) | fn sma_helper(price_ndarray: &Array1<f32>, period: usize) -> Array1<f32>{
function ema_helper (line 18) | fn ema_helper(price_ndarray: &Array1<f32>, period: usize) -> Array1<f32>{
function sma (line 31) | fn sma(price: Vec<f32>, period: usize) -> PyResult<Vec<f32>> {
function ema (line 45) | fn ema(price: Vec<f32>, period: usize, smoothing: f32) -> PyResult<Vec<f...
function rsi (line 61) | fn rsi(price: Vec<f32>, period: usize) -> PyResult<Vec<f32>>{
function macd (line 98) | fn macd(price: Vec<f32>, period_fast: usize, period_slow: usize, period_...
function roc (line 107) | fn roc(price: Vec<f32>, period: usize) -> PyResult<Vec<f32>> {
function atr (line 120) | fn atr(high: Vec<f32>, low: Vec<f32>, close: Vec<f32>, period: usize) ->...
function cmf (line 146) | fn cmf(high: Vec<f32>, low: Vec<f32>, close: Vec<f32>, volume: Vec<f32>,...
function ZenithTA (line 167) | fn ZenithTA(_py: Python, m: &PyModule) -> PyResult<()> {
Condensed preview — 10 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (18K chars).
[
{
"path": ".github/workflows/CI.yml",
"chars": 4573,
"preview": "name: CI\n\non:\n push:\n branches:\n - main\n tags:\n - \"*\"\n pull_request:\n\njobs:\n macos:\n runs-on: maco"
},
{
"path": ".github/workflows/rust.yml",
"chars": 902,
"preview": "name: Rust\n\non:\n push:\n branches: [ main ]\n pull_request:\n branches: [ main ]\n\nenv:\n CARGO_TERM_COLOR: always\n\n"
},
{
"path": ".gitignore",
"chars": 434,
"preview": "# Generated by Cargo\n# will have compiled files and executables\n/target/\n\n# Remove Cargo.lock from gitignore if creating"
},
{
"path": "Cargo.toml",
"chars": 219,
"preview": "[package]\nname = \"ZenithTA\"\nversion = \"1.0.0\"\nedition = \"2021\"\n\n[lib]\nname = \"ZenithTA\"\ncrate-type = [\"cdylib\"]\n\n[depend"
},
{
"path": "LICENSE",
"chars": 1061,
"preview": "MIT License\n\nCopyright (c) 2022 Greg\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof th"
},
{
"path": "README.md",
"chars": 2505,
"preview": "[](https:"
},
{
"path": "pyproject.toml",
"chars": 136,
"preview": "[build-system]\nrequires = [\"maturin>=0.12,<0.13\"]\nbuild-backend = \"maturin\"\n\n[project]\nname = \"ZenithTA\"\ndependencies = "
},
{
"path": "speed_tests/ema.py",
"chars": 538,
"preview": "import pandas_datareader as pdr\nimport pandas as pd\nfrom ZenithTA import *\nfrom timeit import default_timer as timer\nfro"
},
{
"path": "speed_tests/sma.py",
"chars": 481,
"preview": "import pandas_datareader as pdr\nimport pandas as pd\nfrom ZenithTA import *\nfrom timeit import default_timer as timer\nfro"
},
{
"path": "src/lib.rs",
"chars": 6110,
"preview": "use pyo3::prelude::*;\nuse pyo3::wrap_pyfunction;\nuse numpy::ndarray::prelude::*;\n\n//Helper functions for indicators\nfn s"
}
]
About this extraction
This page contains the full source code of the gregyjames/Panther GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 10 files (16.6 KB), approximately 5.0k tokens, and a symbol index with 10 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.