@drico2008/fincli 0.3.1 → 0.4.0

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 (37) hide show
  1. package/README.md +217 -217
  2. package/fincli/__init__.py +1 -1
  3. package/fincli/app/analysis/ai_prompts.py +29 -27
  4. package/fincli/app/analysis/analyzer.py +34 -34
  5. package/fincli/app/analysis/assistant_context.py +3 -3
  6. package/fincli/app/cli/commands.py +33 -27
  7. package/fincli/app/cli/router.py +1633 -1105
  8. package/fincli/app/diagnostics/__init__.py +2 -0
  9. package/fincli/app/diagnostics/capabilities.py +44 -0
  10. package/fincli/app/diagnostics/runtime.py +106 -0
  11. package/fincli/app/main.py +6 -1
  12. package/fincli/app/modules/economic_calendar.py +512 -512
  13. package/fincli/app/modules/portfolio_risk.py +305 -305
  14. package/fincli/app/modules/trading.py +142 -0
  15. package/fincli/app/plugins/loader.py +72 -72
  16. package/fincli/app/providers/market/finnhub_provider.py +51 -2
  17. package/fincli/app/providers/market/symbols.py +95 -2
  18. package/fincli/app/providers/reliability.py +82 -65
  19. package/fincli/app/research/__init__.py +8 -8
  20. package/fincli/app/research/engine.py +119 -112
  21. package/fincli/app/research/exporter.py +91 -91
  22. package/fincli/app/research/formatter.py +25 -24
  23. package/fincli/app/research/models.py +22 -21
  24. package/fincli/app/research/prompt_builder.py +53 -51
  25. package/fincli/app/services/data_quality.py +27 -0
  26. package/fincli/app/services/data_trust.py +117 -0
  27. package/fincli/app/services/macro_data.py +158 -50
  28. package/fincli/app/services/market_data.py +183 -79
  29. package/fincli/app/services/market_overview.py +131 -142
  30. package/fincli/app/services/news_aggregator.py +95 -95
  31. package/fincli/app/storage/config.py +6 -3
  32. package/fincli/app/storage/database.py +130 -117
  33. package/fincli/app/storage/provider_metrics.py +61 -61
  34. package/fincli/app/storage/secrets.py +128 -128
  35. package/npm/bin/fincli.js +65 -65
  36. package/package.json +7 -7
  37. package/pyproject.toml +1 -1
@@ -0,0 +1,2 @@
1
+ """Runtime diagnostics for FinCLI."""
2
+
@@ -0,0 +1,44 @@
1
+ """Command capability matrix for provider/data reliability diagnostics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass(frozen=True, slots=True)
9
+ class CommandCapability:
10
+ command: str
11
+ needs: tuple[str, ...]
12
+ provider_dependent: bool
13
+ note: str
14
+
15
+
16
+ COMMAND_CAPABILITIES: tuple[CommandCapability, ...] = (
17
+ CommandCapability("/research", ("quote", "history", "news", "fundamentals", "ai_optional"), True, "Research center combines all available data."),
18
+ CommandCapability("/market", ("quote", "history", "news", "fundamentals"), True, "Market overview requires quote and OHLCV; news/fundamentals may be partial."),
19
+ CommandCapability("/news", ("news",), True, "Uses news connector priority and RSS/API fallbacks."),
20
+ CommandCapability("/technical", ("history",), True, "Needs OHLCV candles for indicators and signal scoring."),
21
+ CommandCapability("/structure", ("history",), True, "Needs OHLCV candles for market structure."),
22
+ CommandCapability("/mtf", ("history",), True, "Needs history across requested intervals."),
23
+ CommandCapability("/analyze", ("quote", "history", "news", "fundamentals", "ai"), True, "AI analysis is grounded on provider data quality."),
24
+ CommandCapability("/calendar", ("calendar",), True, "Finnhub/public calendar may degrade to schedule-only fallback."),
25
+ CommandCapability("/quote", ("quote",), True, "Fast quote check."),
26
+ CommandCapability("/funda", ("fundamentals",), True, "Fundamental snapshot coverage varies by symbol/provider."),
27
+ CommandCapability("/yahoo", ("yahoo_table",), True, "Yahoo table sections are provider-specific."),
28
+ CommandCapability("/scan", ("watchlist", "history"), True, "Scanner needs watchlist symbols and candles."),
29
+ CommandCapability("/provider test", ("quote",), True, "Live provider smoke check for one symbol."),
30
+ CommandCapability("/web", ("public_web",), True, "Public web search depends on connectivity/search availability."),
31
+ CommandCapability("/ai", ("ai", "market_context_optional", "web_context_optional"), True, "Free chat can use market/web context when detected."),
32
+ CommandCapability("/portfolio risk", ("portfolio", "quote_optional"), True, "Local positions plus quote when available."),
33
+ CommandCapability("/journal review", ("journal", "ai"), True, "Journal analytics plus AI provider."),
34
+ )
35
+
36
+
37
+ def capability_rows() -> list[CommandCapability]:
38
+ return list(COMMAND_CAPABILITIES)
39
+
40
+
41
+ def capability_summary() -> str:
42
+ provider_commands = sum(1 for item in COMMAND_CAPABILITIES if item.provider_dependent)
43
+ unique_needs = sorted({need for item in COMMAND_CAPABILITIES for need in item.needs})
44
+ return f"{len(COMMAND_CAPABILITIES)} command profile(s); provider-dependent={provider_commands}; needs={', '.join(unique_needs)}"
@@ -0,0 +1,106 @@
1
+ """Runtime environment checks used by /doctor and startup guards."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from importlib import metadata, util
7
+ from pathlib import Path
8
+ import platform
9
+ import sys
10
+
11
+ from fincli import __version__
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class RuntimeCheck:
16
+ """Single display-safe runtime diagnostic result."""
17
+
18
+ name: str
19
+ status: str
20
+ detail: str
21
+
22
+
23
+ REQUIRED_MODULES: tuple[tuple[str, str], ...] = (
24
+ ("textual", "Textual TUI runtime"),
25
+ ("rich", "terminal rendering"),
26
+ ("httpx", "HTTP client"),
27
+ ("pydantic", "data validation"),
28
+ ("yfinance", "Yahoo Finance fallback"),
29
+ ("pandas", "tabular market data"),
30
+ ("numpy", "technical calculations"),
31
+ )
32
+
33
+
34
+ def check_runtime_environment() -> list[RuntimeCheck]:
35
+ """Return launch-critical checks without exposing local secrets."""
36
+
37
+ checks = [
38
+ _python_version_check(),
39
+ _package_version_check(),
40
+ _npm_runtime_check(),
41
+ ]
42
+ checks.extend(_dependency_checks())
43
+ checks.append(_user_config_dir_check())
44
+ return checks
45
+
46
+
47
+ def startup_dependency_error(exc: ImportError) -> str:
48
+ """Build a friendly startup message when a runtime dependency is missing."""
49
+
50
+ missing = getattr(exc, "name", None) or "unknown"
51
+ return (
52
+ f"FinCLI gagal memuat dependency Python: {missing}\n\n"
53
+ "Fix cepat:\n"
54
+ "- npm global: npm install -g @drico2008/fincli@latest --registry=https://registry.npmjs.org/\n"
55
+ "- local dev : pip install -e \".[dev]\"\n\n"
56
+ "Jika memakai npm global dan error tetap muncul, hapus install lama lalu install ulang:\n"
57
+ "npm uninstall -g @drico2008/fincli && npm install -g @drico2008/fincli@latest"
58
+ )
59
+
60
+
61
+ def _python_version_check() -> RuntimeCheck:
62
+ version = platform.python_version()
63
+ if sys.version_info >= (3, 11):
64
+ return RuntimeCheck("Python", "ok", f"{version} ({sys.executable})")
65
+ return RuntimeCheck("Python", "error", f"{version}; FinCLI requires Python 3.11+")
66
+
67
+
68
+ def _package_version_check() -> RuntimeCheck:
69
+ try:
70
+ installed = metadata.version("fincli")
71
+ except metadata.PackageNotFoundError:
72
+ return RuntimeCheck("Package Metadata", "warning", f"source import version={__version__}; package metadata not installed")
73
+ if installed == __version__:
74
+ return RuntimeCheck("Package Metadata", "ok", f"fincli {installed}")
75
+ return RuntimeCheck("Package Metadata", "warning", f"package={installed}; import={__version__}")
76
+
77
+
78
+ def _dependency_checks() -> list[RuntimeCheck]:
79
+ checks: list[RuntimeCheck] = []
80
+ for module_name, purpose in REQUIRED_MODULES:
81
+ if util.find_spec(module_name) is None:
82
+ checks.append(RuntimeCheck(f"Dependency:{module_name}", "error", f"missing; required for {purpose}"))
83
+ else:
84
+ checks.append(RuntimeCheck(f"Dependency:{module_name}", "ok", purpose))
85
+ return checks
86
+
87
+
88
+ def _npm_runtime_check() -> RuntimeCheck:
89
+ executable = Path(sys.executable)
90
+ if ".npm-python" not in {part.lower() for part in executable.parts}:
91
+ return RuntimeCheck("npm Runtime", "info", "not running inside npm wrapper venv")
92
+ if executable.exists():
93
+ return RuntimeCheck("npm Runtime", "ok", str(executable))
94
+ return RuntimeCheck("npm Runtime", "error", f"missing python executable: {executable}")
95
+
96
+
97
+ def _user_config_dir_check() -> RuntimeCheck:
98
+ config_dir = Path.home() / ".fincli"
99
+ try:
100
+ config_dir.mkdir(parents=True, exist_ok=True)
101
+ probe = config_dir / ".doctor-write-test"
102
+ probe.write_text("ok", encoding="utf-8")
103
+ probe.unlink(missing_ok=True)
104
+ except OSError as exc:
105
+ return RuntimeCheck("User Config Dir", "error", f"{config_dir}: {exc}")
106
+ return RuntimeCheck("User Config Dir", "ok", str(config_dir))
@@ -2,13 +2,18 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from fincli.app.tui.layout import FinCLIApp
6
5
  from fincli.app.utils.logger import configure_logging
6
+ from fincli.app.diagnostics.runtime import startup_dependency_error
7
7
 
8
8
 
9
9
  def main() -> None:
10
10
  """Run the FinCLI TUI application."""
11
11
  configure_logging()
12
+ try:
13
+ from fincli.app.tui.layout import FinCLIApp
14
+ except ImportError as exc:
15
+ print(startup_dependency_error(exc))
16
+ raise SystemExit(1) from exc
12
17
  FinCLIApp().run()
13
18
 
14
19