grynn-fplot 0.3.1__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.
Files changed (36) hide show
  1. grynn_fplot-0.3.1/.devcontainer/devcontainer.json +28 -0
  2. grynn_fplot-0.3.1/.github/copilot-instructions.md +141 -0
  3. grynn_fplot-0.3.1/.github/dependabot.yml +12 -0
  4. grynn_fplot-0.3.1/.github/workflows/publish.yml +87 -0
  5. grynn_fplot-0.3.1/.gitignore +5 -0
  6. grynn_fplot-0.3.1/.pre-commit-config.yaml +35 -0
  7. grynn_fplot-0.3.1/.secrets.baseline +127 -0
  8. grynn_fplot-0.3.1/.vscode/settings.json +7 -0
  9. grynn_fplot-0.3.1/FILTER_DESIGN.md +316 -0
  10. grynn_fplot-0.3.1/FILTER_EXAMPLES.sh +110 -0
  11. grynn_fplot-0.3.1/Makefile +70 -0
  12. grynn_fplot-0.3.1/PERFORMANCE_OPTIMIZATION.md +170 -0
  13. grynn_fplot-0.3.1/PKG-INFO +166 -0
  14. grynn_fplot-0.3.1/README.md +128 -0
  15. grynn_fplot-0.3.1/SUMMARY.md +257 -0
  16. grynn_fplot-0.3.1/WEB_REFACTOR.md +175 -0
  17. grynn_fplot-0.3.1/example_usage.sh +52 -0
  18. grynn_fplot-0.3.1/grynn_fplot/__init__.py +1 -0
  19. grynn_fplot-0.3.1/grynn_fplot/cli.py +442 -0
  20. grynn_fplot-0.3.1/grynn_fplot/core.py +575 -0
  21. grynn_fplot-0.3.1/grynn_fplot/drawdowns.py +80 -0
  22. grynn_fplot-0.3.1/grynn_fplot/filter_parser.py +453 -0
  23. grynn_fplot-0.3.1/grynn_fplot/index.html +967 -0
  24. grynn_fplot-0.3.1/grynn_fplot/plot_option_interactive.py +57 -0
  25. grynn_fplot-0.3.1/grynn_fplot/serve.py +221 -0
  26. grynn_fplot-0.3.1/grynn_fplot/web_api.py +292 -0
  27. grynn_fplot-0.3.1/pyproject.toml +66 -0
  28. grynn_fplot-0.3.1/tests/test_cli_options.py +124 -0
  29. grynn_fplot-0.3.1/tests/test_filter_evaluation.py +174 -0
  30. grynn_fplot-0.3.1/tests/test_filter_parser.py +320 -0
  31. grynn_fplot-0.3.1/tests/test_fplot.py +68 -0
  32. grynn_fplot-0.3.1/tests/test_options.py +127 -0
  33. grynn_fplot-0.3.1/tests/test_web_api.py +274 -0
  34. grynn_fplot-0.3.1/tests/test_web_interface.py +301 -0
  35. grynn_fplot-0.3.1/todo.md +1 -0
  36. grynn_fplot-0.3.1/uv.lock +1761 -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,87 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ release:
8
+ types: [published]
9
+ workflow_dispatch:
10
+ inputs:
11
+ target:
12
+ description: 'Publish target'
13
+ required: true
14
+ default: 'testpypi'
15
+ type: choice
16
+ options:
17
+ - testpypi
18
+ - pypi
19
+
20
+ jobs:
21
+ build:
22
+ name: Build distribution
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+
27
+ - name: Install uv
28
+ uses: astral-sh/setup-uv@v4
29
+ with:
30
+ version: "latest"
31
+
32
+ - name: Set up Python
33
+ run: uv python install 3.13
34
+
35
+ - name: Sync dependencies
36
+ run: uv sync
37
+
38
+ - name: Build package
39
+ run: uv build
40
+
41
+ - name: Store distribution packages
42
+ uses: actions/upload-artifact@v4
43
+ with:
44
+ name: python-package-distributions
45
+ path: dist/
46
+
47
+ publish-testpypi:
48
+ name: Publish to TestPyPI
49
+ if: github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'testpypi'
50
+ needs: build
51
+ runs-on: ubuntu-latest
52
+ environment:
53
+ name: testpypi
54
+ url: https://test.pypi.org/p/grynn-fplot
55
+ permissions:
56
+ id-token: write
57
+ steps:
58
+ - name: Download distributions
59
+ uses: actions/download-artifact@v4
60
+ with:
61
+ name: python-package-distributions
62
+ path: dist/
63
+
64
+ - name: Publish to TestPyPI
65
+ uses: pypa/gh-action-pypi-publish@release/v1
66
+ with:
67
+ repository-url: https://test.pypi.org/legacy/
68
+
69
+ publish-pypi:
70
+ name: Publish to PyPI
71
+ if: github.event_name == 'push' || github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'pypi')
72
+ needs: build
73
+ runs-on: ubuntu-latest
74
+ environment:
75
+ name: pypi
76
+ url: https://pypi.org/p/grynn-fplot
77
+ permissions:
78
+ id-token: write
79
+ steps:
80
+ - name: Download distributions
81
+ uses: actions/download-artifact@v4
82
+ with:
83
+ name: python-package-distributions
84
+ path: dist/
85
+
86
+ - name: Publish to PyPI
87
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,5 @@
1
+ .venv/
2
+ __pycache__/
3
+ build/
4
+ dist/
5
+ *.egg-info/
@@ -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,7 @@
1
+ {
2
+ "python.testing.pytestArgs": [
3
+ "."
4
+ ],
5
+ "python.testing.unittestEnabled": false,
6
+ "python.testing.pytestEnabled": true
7
+ }