grynn-fplot 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- grynn_fplot-0.3.4/.devcontainer/devcontainer.json +28 -0
- grynn_fplot-0.3.4/.github/copilot-instructions.md +141 -0
- grynn_fplot-0.3.4/.github/dependabot.yml +12 -0
- grynn_fplot-0.3.4/.github/workflows/publish.yml +56 -0
- grynn_fplot-0.3.4/.gitignore +5 -0
- grynn_fplot-0.3.4/.pre-commit-config.yaml +35 -0
- grynn_fplot-0.3.4/.secrets.baseline +127 -0
- grynn_fplot-0.3.4/.vscode/settings.json +7 -0
- grynn_fplot-0.3.4/Makefile +70 -0
- grynn_fplot-0.3.4/PKG-INFO +188 -0
- grynn_fplot-0.3.4/README.md +149 -0
- grynn_fplot-0.3.4/docs/FILTER_DESIGN.md +316 -0
- grynn_fplot-0.3.4/docs/FILTER_EXAMPLES.sh +110 -0
- grynn_fplot-0.3.4/docs/PERFORMANCE_OPTIMIZATION.md +170 -0
- grynn_fplot-0.3.4/docs/WEB_REFACTOR.md +175 -0
- grynn_fplot-0.3.4/example_usage.sh +52 -0
- grynn_fplot-0.3.4/grynn_fplot/__init__.py +1 -0
- grynn_fplot-0.3.4/grynn_fplot/cli.py +607 -0
- grynn_fplot-0.3.4/grynn_fplot/core.py +844 -0
- grynn_fplot-0.3.4/grynn_fplot/drawdowns.py +80 -0
- grynn_fplot-0.3.4/grynn_fplot/filter_parser.py +579 -0
- grynn_fplot-0.3.4/grynn_fplot/index.html +967 -0
- grynn_fplot-0.3.4/grynn_fplot/plot_option_interactive.py +57 -0
- grynn_fplot-0.3.4/grynn_fplot/serve.py +221 -0
- grynn_fplot-0.3.4/grynn_fplot/web_api.py +292 -0
- grynn_fplot-0.3.4/pyproject.toml +67 -0
- grynn_fplot-0.3.4/tests/test_candlestick.py +143 -0
- grynn_fplot-0.3.4/tests/test_cli_integration.py +123 -0
- grynn_fplot-0.3.4/tests/test_cli_options.py +124 -0
- grynn_fplot-0.3.4/tests/test_filter_evaluation.py +174 -0
- grynn_fplot-0.3.4/tests/test_filter_parser.py +320 -0
- grynn_fplot-0.3.4/tests/test_fplot.py +68 -0
- grynn_fplot-0.3.4/tests/test_input_parsing.py +151 -0
- grynn_fplot-0.3.4/tests/test_leverage.py +167 -0
- grynn_fplot-0.3.4/tests/test_options.py +127 -0
- grynn_fplot-0.3.4/tests/test_web_api.py +274 -0
- grynn_fplot-0.3.4/tests/test_web_interface.py +301 -0
- grynn_fplot-0.3.4/uv.lock +1776 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
2
|
+
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
|
3
|
+
{
|
|
4
|
+
"name": "Python 3",
|
|
5
|
+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
6
|
+
"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
|
|
7
|
+
"features": {
|
|
8
|
+
"ghcr.io/devcontainers-extra/features/uv:1": {}
|
|
9
|
+
},
|
|
10
|
+
// Mount .venv to a named volume to separate container and host environments
|
|
11
|
+
// Copy git config from host (not synced, just copy)
|
|
12
|
+
"mounts": [
|
|
13
|
+
"source=grynn_fplot_venv,target=${containerWorkspaceFolder}/.venv,type=volume",
|
|
14
|
+
"source=${localEnv:HOME}/.gitconfig,target=/tmp/host-gitconfig,type=bind,readonly"
|
|
15
|
+
],
|
|
16
|
+
// Use 'postCreateCommand' to run commands after the container is created.
|
|
17
|
+
"postCreateCommand": "sudo chown -R vscode:vscode ${containerWorkspaceFolder}/.venv && cp /tmp/host-gitconfig ~/.gitconfig || true",
|
|
18
|
+
// Configure tool-specific properties.
|
|
19
|
+
"customizations": {
|
|
20
|
+
"vscode": {
|
|
21
|
+
"settings": {
|
|
22
|
+
"chat.tools.global.autoApprove": true
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
27
|
+
// "remoteUser": "root"
|
|
28
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Financial Plotting CLI Tool
|
|
2
|
+
Financial plotting CLI tool (fplot) is a Python application that provides both a command-line interface and a web interface for plotting stock price data, drawdowns, and financial analysis. Built with Python 3.12+, matplotlib, FastAPI, and managed with uv package manager.
|
|
3
|
+
|
|
4
|
+
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
|
|
5
|
+
|
|
6
|
+
## Working Effectively
|
|
7
|
+
|
|
8
|
+
### Bootstrap and Setup
|
|
9
|
+
- Install uv package manager: `pip install uv`
|
|
10
|
+
- Bootstrap the development environment: `make dev`
|
|
11
|
+
- Takes ~3 minutes for fresh setup. NEVER CANCEL. Set timeout to 10+ minutes.
|
|
12
|
+
- Creates virtual environment and installs all dependencies including scipy, matplotlib, pandas
|
|
13
|
+
- Subsequent runs take <1 second due to caching
|
|
14
|
+
- Install CLI tool system-wide: `make install`
|
|
15
|
+
- Takes ~3 minutes for fresh install. NEVER CANCEL. Set timeout to 10+ minutes.
|
|
16
|
+
- Makes `fplot` command available globally via uv tool install
|
|
17
|
+
|
|
18
|
+
### Testing and Quality
|
|
19
|
+
- Run tests: `make test` or `uv run pytest`
|
|
20
|
+
- Takes ~6 seconds. Set timeout to 30+ seconds.
|
|
21
|
+
- All tests must pass before committing changes
|
|
22
|
+
- Run linter: `make lint` or `uvx ruff check`
|
|
23
|
+
- Takes <1 second. Set timeout to 30+ seconds.
|
|
24
|
+
- Code must pass linting before committing changes
|
|
25
|
+
- Clean build artifacts: `make clean`
|
|
26
|
+
|
|
27
|
+
### Build and Development
|
|
28
|
+
- Development setup: `make dev`
|
|
29
|
+
- NEVER CANCEL: Takes up to 3 minutes for fresh setup. Set timeout to 10+ minutes.
|
|
30
|
+
- Downloads and builds many packages including scipy (35MB+), matplotlib, pandas
|
|
31
|
+
- Subsequent runs are nearly instantaneous due to uv caching
|
|
32
|
+
- The project has no separate "build" step - it's a Python package installed directly
|
|
33
|
+
|
|
34
|
+
## Running the Application
|
|
35
|
+
|
|
36
|
+
### CLI Tool
|
|
37
|
+
- Basic usage: `fplot <ticker> [options]`
|
|
38
|
+
- Examples:
|
|
39
|
+
- `fplot AAPL` - Plot Apple stock (last 1 year default)
|
|
40
|
+
- `fplot AAPL --since "last 30 days"` - Plot Apple stock for last 30 days
|
|
41
|
+
- `fplot AAPL,TSLA --since "mar 2023"` - Compare multiple tickers
|
|
42
|
+
- `fplot --version` - Show version
|
|
43
|
+
- `fplot --help` - Show help
|
|
44
|
+
|
|
45
|
+
### Web Interface
|
|
46
|
+
- Start web server: `uv run python grynn_fplot/serve.py`
|
|
47
|
+
- Runs on http://0.0.0.0:8000
|
|
48
|
+
- Provides interactive HTML interface with Plotly charts
|
|
49
|
+
- Hot reloads on code changes in development
|
|
50
|
+
|
|
51
|
+
### Network Requirements
|
|
52
|
+
- CLI and web server require internet access to download financial data from Yahoo Finance
|
|
53
|
+
- Without internet, commands will fail with DNS resolution errors (expected behavior)
|
|
54
|
+
- All core functionality works offline with sample data
|
|
55
|
+
|
|
56
|
+
## Validation
|
|
57
|
+
|
|
58
|
+
### Always Test After Making Changes
|
|
59
|
+
1. **Run the full test suite**: `make test` - must pass completely
|
|
60
|
+
2. **Run the linter**: `make lint` - must pass without issues
|
|
61
|
+
3. **Test CLI functionality**:
|
|
62
|
+
- `fplot --version` should show current version
|
|
63
|
+
- `fplot --help` should show usage information
|
|
64
|
+
- If internet is available, test with real ticker: `fplot AAPL --since "last 7 days"`
|
|
65
|
+
4. **Test web server**:
|
|
66
|
+
- Start server: `uv run python grynn_fplot/serve.py`
|
|
67
|
+
- Verify http://localhost:8000 loads the HTML interface
|
|
68
|
+
- Check that page contains "Stock Chart" and plotly references
|
|
69
|
+
5. **Test core functionality with sample data**:
|
|
70
|
+
- All core functions (normalize_prices, calculate_drawdowns, calculate_cagr, etc.) work correctly
|
|
71
|
+
- Date parsing handles various formats correctly
|
|
72
|
+
|
|
73
|
+
### Manual Validation Scenarios
|
|
74
|
+
- **CLI Workflow**: `fplot --version` → `fplot --help` → verify output is correct
|
|
75
|
+
- **Web Workflow**: Start server → verify homepage loads → check HTML contains expected UI elements
|
|
76
|
+
- **Date Parsing**: Test with "last 30 days", "YTD", "last 6 months", "2 years ago"
|
|
77
|
+
|
|
78
|
+
## Common Tasks
|
|
79
|
+
|
|
80
|
+
### Key Project Structure
|
|
81
|
+
```
|
|
82
|
+
/home/runner/work/grynn_cli_fplot/grynn_cli_fplot/
|
|
83
|
+
├── grynn_fplot/ # Main package
|
|
84
|
+
│ ├── cli.py # CLI command implementation
|
|
85
|
+
│ ├── core.py # Core financial functions
|
|
86
|
+
│ ├── serve.py # FastAPI web server
|
|
87
|
+
│ └── index.html # Web UI template
|
|
88
|
+
├── tests/ # Test suite
|
|
89
|
+
├── Makefile # Build automation
|
|
90
|
+
├── pyproject.toml # Package configuration
|
|
91
|
+
└── uv.lock # Dependency lock file
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Dependencies and External Libraries
|
|
95
|
+
- **Package Manager**: uv (modern Python package manager)
|
|
96
|
+
- **Core Dependencies**: click, matplotlib, pandas, yfinance, scikit-learn
|
|
97
|
+
- **Web Dependencies**: fastapi, uvicorn
|
|
98
|
+
- **Dev Dependencies**: pytest, ruff, mypy
|
|
99
|
+
- **External Git Dependencies**:
|
|
100
|
+
- grynn-pylib from https://github.com/Grynn/grynn_pylib.git
|
|
101
|
+
- yfinance from https://github.com/ranaroussi/yfinance.git
|
|
102
|
+
|
|
103
|
+
### Timing Expectations and Timeouts
|
|
104
|
+
- **CRITICAL**: NEVER CANCEL builds or dependency installations - they may take 3+ minutes
|
|
105
|
+
- **make dev**: 3 minutes fresh, <1 second cached. Timeout: 10+ minutes.
|
|
106
|
+
- **make install**: 3 minutes fresh, <1 second cached. Timeout: 10+ minutes.
|
|
107
|
+
- **make test**: 6 seconds. Timeout: 30+ seconds.
|
|
108
|
+
- **make lint**: <1 second. Timeout: 30+ seconds.
|
|
109
|
+
- **CLI commands**: <5 seconds (plus network time for data). Timeout: 60+ seconds.
|
|
110
|
+
- **Web server startup**: 3 seconds. Timeout: 30+ seconds.
|
|
111
|
+
|
|
112
|
+
### Key Files to Monitor
|
|
113
|
+
- Always check `pyproject.toml` when changing dependencies
|
|
114
|
+
- Always check `grynn_fplot/core.py` when modifying financial calculations
|
|
115
|
+
- Always check `grynn_fplot/cli.py` when changing CLI behavior
|
|
116
|
+
- Always check `tests/test_fplot.py` when adding new functionality
|
|
117
|
+
|
|
118
|
+
### Development Environment Details
|
|
119
|
+
- **Python Version**: 3.12+ required
|
|
120
|
+
- **Virtual Environment**: Managed by uv in `.venv/`
|
|
121
|
+
- **Package Installation**: Uses `uv tool install` for global CLI access
|
|
122
|
+
- **Code Style**: Enforced by ruff linter with 120 character line length
|
|
123
|
+
- **Testing**: pytest-based with unittest-style test classes
|
|
124
|
+
|
|
125
|
+
## Troubleshooting
|
|
126
|
+
|
|
127
|
+
### Common Issues
|
|
128
|
+
- **ModuleNotFoundError**: Run `make dev` to ensure all dependencies are installed
|
|
129
|
+
- **DNS/Network Errors**: Expected when internet is limited - test with sample data instead
|
|
130
|
+
- **Build timeouts**: Increase timeout values - builds legitimately take 3+ minutes fresh
|
|
131
|
+
- **Import errors**: Use `uv run` prefix for all Python commands to use virtual environment
|
|
132
|
+
|
|
133
|
+
### Build Problems
|
|
134
|
+
- Clean and rebuild: `make clean && make dev`
|
|
135
|
+
- Check uv version: `uv --version` (should be 0.8+)
|
|
136
|
+
- Verify Python version: `python3 --version` (should be 3.12+)
|
|
137
|
+
|
|
138
|
+
### Testing Without Network
|
|
139
|
+
- CLI will show DNS errors but this is expected behavior
|
|
140
|
+
- Use core functionality tests with sample data to verify logic
|
|
141
|
+
- Web server will start but data endpoints will fail (expected)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for more information:
|
|
4
|
+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
5
|
+
# https://containers.dev/guide/dependabot
|
|
6
|
+
|
|
7
|
+
version: 2
|
|
8
|
+
updates:
|
|
9
|
+
- package-ecosystem: "devcontainers"
|
|
10
|
+
directory: "/"
|
|
11
|
+
schedule:
|
|
12
|
+
interval: weekly
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
release:
|
|
8
|
+
types: [published]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: Build distribution
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v4
|
|
20
|
+
with:
|
|
21
|
+
version: "latest"
|
|
22
|
+
|
|
23
|
+
- name: Set up Python
|
|
24
|
+
run: uv python install 3.13
|
|
25
|
+
|
|
26
|
+
- name: Sync dependencies
|
|
27
|
+
run: uv sync
|
|
28
|
+
|
|
29
|
+
- name: Build package
|
|
30
|
+
run: uv build
|
|
31
|
+
|
|
32
|
+
- name: Store distribution packages
|
|
33
|
+
uses: actions/upload-artifact@v4
|
|
34
|
+
with:
|
|
35
|
+
name: python-package-distributions
|
|
36
|
+
path: dist/
|
|
37
|
+
|
|
38
|
+
publish-pypi:
|
|
39
|
+
name: Publish to PyPI
|
|
40
|
+
if: github.event_name == 'push' || github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'pypi')
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
environment:
|
|
44
|
+
name: pypi
|
|
45
|
+
url: https://pypi.org/p/grynn-fplot
|
|
46
|
+
permissions:
|
|
47
|
+
id-token: write
|
|
48
|
+
steps:
|
|
49
|
+
- name: Download distributions
|
|
50
|
+
uses: actions/download-artifact@v4
|
|
51
|
+
with:
|
|
52
|
+
name: python-package-distributions
|
|
53
|
+
path: dist/
|
|
54
|
+
|
|
55
|
+
- name: Publish to PyPI
|
|
56
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
- id: check-toml
|
|
10
|
+
|
|
11
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
12
|
+
rev: v0.9.2
|
|
13
|
+
hooks:
|
|
14
|
+
- id: ruff
|
|
15
|
+
name: ruff-lint
|
|
16
|
+
args: ["--fix"]
|
|
17
|
+
- id: ruff-format
|
|
18
|
+
name: ruff-format
|
|
19
|
+
|
|
20
|
+
- repo: https://github.com/Yelp/detect-secrets
|
|
21
|
+
rev: v1.5.0
|
|
22
|
+
hooks:
|
|
23
|
+
- id: detect-secrets
|
|
24
|
+
name: detect-secrets
|
|
25
|
+
args: ['--baseline', '.secrets.baseline']
|
|
26
|
+
exclude: package.lock.json
|
|
27
|
+
|
|
28
|
+
- repo: local
|
|
29
|
+
hooks:
|
|
30
|
+
- id: pytest
|
|
31
|
+
name: Run tests
|
|
32
|
+
entry: uv run pytest
|
|
33
|
+
language: system
|
|
34
|
+
pass_filenames: false
|
|
35
|
+
always_run: true
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.5.0",
|
|
3
|
+
"plugins_used": [
|
|
4
|
+
{
|
|
5
|
+
"name": "ArtifactoryDetector"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"name": "AWSKeyDetector"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "AzureStorageKeyDetector"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "Base64HighEntropyString",
|
|
15
|
+
"limit": 4.5
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "BasicAuthDetector"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "CloudantDetector"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "DiscordBotTokenDetector"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "GitHubTokenDetector"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "GitLabTokenDetector"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "HexHighEntropyString",
|
|
34
|
+
"limit": 3.0
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "IbmCloudIamDetector"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "IbmCosHmacDetector"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "IPPublicDetector"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "JwtTokenDetector"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "KeywordDetector",
|
|
50
|
+
"keyword_exclude": ""
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "MailchimpDetector"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "NpmDetector"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "OpenAIDetector"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "PrivateKeyDetector"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "PypiTokenDetector"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "SendGridDetector"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "SlackDetector"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "SoftlayerDetector"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "SquareOAuthDetector"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "StripeDetector"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "TelegramBotTokenDetector"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": "TwilioKeyDetector"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"filters_used": [
|
|
90
|
+
{
|
|
91
|
+
"path": "detect_secrets.filters.allowlist.is_line_allowlisted"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
|
|
95
|
+
"min_level": 2
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"path": "detect_secrets.filters.heuristic.is_indirect_reference"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"path": "detect_secrets.filters.heuristic.is_likely_id_string"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"path": "detect_secrets.filters.heuristic.is_lock_file"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"path": "detect_secrets.filters.heuristic.is_potential_uuid"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"path": "detect_secrets.filters.heuristic.is_sequential_string"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"path": "detect_secrets.filters.heuristic.is_swagger_file"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"path": "detect_secrets.filters.heuristic.is_templated_secret"
|
|
123
|
+
}
|
|
124
|
+
],
|
|
125
|
+
"results": {},
|
|
126
|
+
"generated_at": "2025-09-19T21:03:14Z"
|
|
127
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
.PHONY: install clean test lint format coverage dev bump pre-commit pre-commit-install build publish
|
|
2
|
+
|
|
3
|
+
test:
|
|
4
|
+
uv run pytest
|
|
5
|
+
|
|
6
|
+
pre-commit: dev
|
|
7
|
+
uvx pre-commit run --all-files
|
|
8
|
+
|
|
9
|
+
pre-commit-install:
|
|
10
|
+
uvx pre-commit install
|
|
11
|
+
|
|
12
|
+
work-tree-is-clean:
|
|
13
|
+
@# Check if the working directory is clean
|
|
14
|
+
@if [ -n "$$(git status --porcelain)" ]; then \
|
|
15
|
+
echo "Error: Working directory is not clean. Please commit or stash your changes first."; \
|
|
16
|
+
git status --short; \
|
|
17
|
+
exit 1; \
|
|
18
|
+
else \
|
|
19
|
+
echo "Working directory is clean."; \
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
bump: test pre-commit-install work-tree-is-clean
|
|
23
|
+
@# 1. Bump version using uv
|
|
24
|
+
@uv version --bump patch
|
|
25
|
+
@# 2. Commit changes (work tree was clean before, so only version files changed)
|
|
26
|
+
@new_version=$$(uv version --short) && \
|
|
27
|
+
git commit -am "Bump version => v$$new_version" && \
|
|
28
|
+
git tag "v$$new_version" && \
|
|
29
|
+
echo "Created git commit and tag v$$new_version"
|
|
30
|
+
|
|
31
|
+
install:
|
|
32
|
+
uv tool install --upgrade-package "grynn_fplot" "grynn_fplot @ $$PWD"
|
|
33
|
+
|
|
34
|
+
dev:
|
|
35
|
+
uv sync --all-extras
|
|
36
|
+
|
|
37
|
+
coverage: dev
|
|
38
|
+
uv run pytest --cov=src/grynn_cli_fplot --cov-report=term-missing
|
|
39
|
+
|
|
40
|
+
lint: dev
|
|
41
|
+
uvx ruff check
|
|
42
|
+
|
|
43
|
+
format: dev
|
|
44
|
+
uvx ruff format
|
|
45
|
+
|
|
46
|
+
clean:
|
|
47
|
+
find . -type d -name "__pycache__" -exec rm -rf {} +
|
|
48
|
+
find . -type f -name "*.pyc" -delete
|
|
49
|
+
find . -type f -name "*.pyo" -delete
|
|
50
|
+
find . -type f -name "*.pyd" -delete
|
|
51
|
+
find . -type f -name ".coverage" -delete
|
|
52
|
+
find . -type d -name "*.egg-info" -exec rm -rf {} +
|
|
53
|
+
find . -type d -name "*.egg" -exec rm -rf {} +
|
|
54
|
+
find . -type d -name ".pytest_cache" -exec rm -rf {} +
|
|
55
|
+
find . -type d -name ".mypy_cache" -exec rm -rf {} +
|
|
56
|
+
find . -type d -name ".ruff_cache" -exec rm -rf {} +
|
|
57
|
+
rm -rf ./dist
|
|
58
|
+
rm -rf ./build
|
|
59
|
+
rm -rf ./venv
|
|
60
|
+
rm -rf ./.venv
|
|
61
|
+
rm -rf .coverage
|
|
62
|
+
rm -rf htmlcov
|
|
63
|
+
|
|
64
|
+
build: clean
|
|
65
|
+
uv build
|
|
66
|
+
|
|
67
|
+
publish: build
|
|
68
|
+
@echo "Publishing to PyPI..."
|
|
69
|
+
@echo "Note: For CI/CD, use GitHub Actions with trusted publishing instead."
|
|
70
|
+
uv publish
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: grynn-fplot
|
|
3
|
+
Version: 0.3.4
|
|
4
|
+
Summary: CLI tool for plotting financial data and analyzing options
|
|
5
|
+
Project-URL: Homepage, https://github.com/Grynn/grynn_cli_fplot
|
|
6
|
+
Project-URL: Issues, https://github.com/Grynn/grynn_cli_fplot/issues
|
|
7
|
+
Project-URL: Repository, https://github.com/Grynn/grynn_cli_fplot
|
|
8
|
+
Author-email: Grynn <vishal.doshi@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: cli,finance,options,plotting,stocks
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Requires-Dist: click~=8.1.7
|
|
20
|
+
Requires-Dist: dateparser~=1.2.0
|
|
21
|
+
Requires-Dist: fastapi~=0.115.0
|
|
22
|
+
Requires-Dist: grynn-pylib
|
|
23
|
+
Requires-Dist: ipywidgets>=8.1.5
|
|
24
|
+
Requires-Dist: matplotlib>=3.9.2
|
|
25
|
+
Requires-Dist: mplcursors~=0.5.3
|
|
26
|
+
Requires-Dist: mplfinance>=0.12.9
|
|
27
|
+
Requires-Dist: pandas>=2.2.3
|
|
28
|
+
Requires-Dist: scikit-learn>=1.6.1
|
|
29
|
+
Requires-Dist: tabulate>=0.9.0
|
|
30
|
+
Requires-Dist: uvicorn~=0.32.0
|
|
31
|
+
Requires-Dist: yfinance
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: ipykernel>=6.29.5; extra == 'dev'
|
|
34
|
+
Requires-Dist: ipython>=8.18.1; extra == 'dev'
|
|
35
|
+
Requires-Dist: mypy>=1.14.1; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=8.3.4; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.9.2; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# fplot - Financial Plotting & Options Analysis CLI
|
|
41
|
+
|
|
42
|
+
[](https://pypi.org/project/grynn-fplot/)
|
|
43
|
+
[](https://pypi.org/project/grynn-fplot/)
|
|
44
|
+
|
|
45
|
+
A command-line tool for plotting comparative stock price history and analyzing options contracts.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
### From PyPI
|
|
50
|
+
|
|
51
|
+
```shell
|
|
52
|
+
pip install grynn-fplot
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or with uv:
|
|
56
|
+
|
|
57
|
+
```shell
|
|
58
|
+
uv tool install grynn-fplot
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### From Source
|
|
62
|
+
|
|
63
|
+
For development, install the package in editable mode:
|
|
64
|
+
|
|
65
|
+
```shell
|
|
66
|
+
make dev
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or install locally:
|
|
70
|
+
|
|
71
|
+
```shell
|
|
72
|
+
make install # Uses uv tool install .
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
### Stock Plotting
|
|
78
|
+
|
|
79
|
+
```shell
|
|
80
|
+
fplot <ticker> [--since <date>] [--interval <interval>]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Examples:
|
|
84
|
+
|
|
85
|
+
- `fplot AAPL`
|
|
86
|
+
- `fplot AAPL --since 2020`
|
|
87
|
+
- `fplot AAPL,TSLA --since "mar 2023"`
|
|
88
|
+
|
|
89
|
+
### Options Listing
|
|
90
|
+
|
|
91
|
+
```shell
|
|
92
|
+
fplot <ticker> --call # List call options (default: 6 months max)
|
|
93
|
+
fplot <ticker> --put # List put options (default: 6 months max)
|
|
94
|
+
fplot <ticker> --call --max 3m # List calls with 3 month max expiry
|
|
95
|
+
fplot <ticker> --put --all # List all available put options
|
|
96
|
+
fplot <ticker> --call --min-dte 1y # List long-dated calls (min 1 year)
|
|
97
|
+
fplot <ticker> --call --filter "dte>1y" # Filter using time expressions
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Examples:
|
|
101
|
+
|
|
102
|
+
- `fplot AAPL --call`
|
|
103
|
+
- `fplot TSLA --put --max 3m`
|
|
104
|
+
- `fplot AAPL --call --all`
|
|
105
|
+
- `fplot AAPL --call --min-dte 1y` # Long-dated calls (1+ year)
|
|
106
|
+
- `fplot AAPL --call --min-dte 6m` # Calls with 6+ months to expiry
|
|
107
|
+
- `fplot AAPL --call --filter "dte>10, dte<50"` # 10-50 days to expiry
|
|
108
|
+
- `fplot AAPL --call --filter "dte>1y"` # Options with 1+ year to expiry
|
|
109
|
+
|
|
110
|
+
The options output includes pricing, return metrics, implied leverage, and efficiency:
|
|
111
|
+
```
|
|
112
|
+
AAPL 225C 35DTE ($5.25, 18.5%, 19.0x, eff:45)
|
|
113
|
+
AAPL 230C 35DTE ($3.10, 25.2%, 32.3x, eff:78)
|
|
114
|
+
AAPL 235C 35DTE ($1.85, 35.1%, 54.1x, eff:92)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Format: `TICKER STRIKE[C|P] DAYS_TO_EXPIRY (price, return_metric, leverage, eff:percentile)`
|
|
118
|
+
- For calls: return_metric is CAGR to breakeven
|
|
119
|
+
- For puts: return_metric is annualized return
|
|
120
|
+
- Leverage: Implied leverage (Ω = Δ × S/O) where Δ is Black-Scholes delta, S is spot price, O is option price
|
|
121
|
+
- Delta calculated using actual implied volatility from Yahoo Finance
|
|
122
|
+
- Shows "N/A" if implied volatility is not available
|
|
123
|
+
- Shows the percentage change in option value for a 1% change in stock price
|
|
124
|
+
- Example: 10x leverage means a 1% stock move results in ~10% option move
|
|
125
|
+
- Efficiency: Percentile rank (0-100) of leverage/CAGR ratio
|
|
126
|
+
- Higher = better (high leverage with low required stock movement)
|
|
127
|
+
- 80+ = top 20% most efficient options
|
|
128
|
+
- Shows "N/A" if leverage or return unavailable
|
|
129
|
+
|
|
130
|
+
**Expiry Filtering Options:**
|
|
131
|
+
- `--max <time>`: Filter to show only options expiring within the specified time
|
|
132
|
+
- Examples: `3m` (3 months), `6m` (6 months), `1y` (1 year), `2w` (2 weeks), `30d` (30 days)
|
|
133
|
+
- Default: `6m` (6 months)
|
|
134
|
+
- `--min-dte <time>`: Minimum days to expiry (useful for long-dated options)
|
|
135
|
+
- Accepts plain days or time expressions: `300`, `1y`, `1.5y`, `6m`, `2w`
|
|
136
|
+
- Examples: `--min-dte 1y` (1+ year), `--min-dte 6m` (6+ months)
|
|
137
|
+
- Note: Using `--min-dte` automatically enables `--all` behavior
|
|
138
|
+
- `--all`: Show all available expiries (overrides `--max`)
|
|
139
|
+
|
|
140
|
+
**Advanced Filtering with `--filter`:**
|
|
141
|
+
|
|
142
|
+
The `--filter` option supports complex filter expressions with logical operators:
|
|
143
|
+
|
|
144
|
+
- **Syntax:**
|
|
145
|
+
- Comma (`,`) represents AND operation
|
|
146
|
+
- Plus (`+`) represents OR operation
|
|
147
|
+
- Comparison operators: `>`, `<`, `>=`, `<=`, `=`, `!=`
|
|
148
|
+
- Parentheses for grouping: `(expr1 + expr2), expr3`
|
|
149
|
+
|
|
150
|
+
- **Filter Fields:**
|
|
151
|
+
- `dte`: Days to expiry
|
|
152
|
+
- `volume`: Option volume
|
|
153
|
+
- `price`: Last price
|
|
154
|
+
- `return`, `ret`, `ar`: Return metric (CAGR for calls, annualized return for puts) - all aliases work
|
|
155
|
+
- `strike_pct`, `sp`: Strike percentage above/below spot (positive = above spot, negative = below spot)
|
|
156
|
+
- `lt_days`: Days since last trade (useful for filtering stale options)
|
|
157
|
+
- `leverage`, `lev`: Implied leverage (Ω = Δ × S/O, using Black-Scholes delta)
|
|
158
|
+
- `efficiency`, `eff`: Efficiency percentile (leverage/CAGR ratio, 0-100 scale)
|
|
159
|
+
|
|
160
|
+
- **Examples:**
|
|
161
|
+
- `--filter "dte>300"` - Options with more than 300 days to expiry
|
|
162
|
+
- `--filter "dte>10, dte<50"` - Options between 10-50 days (AND operation)
|
|
163
|
+
- `--filter "dte<30 + dte>300"` - Short-term OR long-dated (OR operation)
|
|
164
|
+
- `--filter "sp>5, sp<15"` - Strikes 5-15% above current spot price
|
|
165
|
+
- `--filter "(dte>300 + dte<30), sp>5"` - Complex nested filters
|
|
166
|
+
- `--filter "volume>=100"` - High volume options
|
|
167
|
+
- `--filter "lt_days<=7"` - Options traded within last 7 days
|
|
168
|
+
- `--filter "ar>50"` - Annualized return > 50%
|
|
169
|
+
- `--filter "leverage>10"` - High leverage options (10x or more)
|
|
170
|
+
- `--filter "lev>5, lev<20"` - Moderate leverage options (5x-20x)
|
|
171
|
+
- `--filter "efficiency>80"` - Top 20% most efficient options
|
|
172
|
+
- `--filter "eff>60, dte<60"` - Above-average efficiency with <60 days to expiry
|
|
173
|
+
|
|
174
|
+
- **Time Values:**
|
|
175
|
+
- DTE-style expressions: `1y` (365 days), `6m` (180 days), `2w` (14 days)
|
|
176
|
+
- Duration expressions: `2d15h`, `30m`, `1d` (converted to hours for duration fields)
|
|
177
|
+
- Examples:
|
|
178
|
+
- `--filter "dte>1y"` - Options with more than 1 year to expiry
|
|
179
|
+
- `--filter "dte>6m"` - Options with more than 6 months to expiry
|
|
180
|
+
- `--filter "lt_days<=7"` - Options traded in the last week
|
|
181
|
+
|
|
182
|
+
Options data is cached for 1 hour to improve performance and reduce API calls.
|
|
183
|
+
|
|
184
|
+
## TODO
|
|
185
|
+
|
|
186
|
+
- fplot --call | fzf lets you select a call; once a call is selected it should be possible to plot a chart for it (yfinance provides price history for option identifiers)
|
|
187
|
+
- need more examples of usage with fzf to pick a call
|
|
188
|
+
- use grynn_pylib to download options (does it have identifiers that can be used to fetch price history?)
|