kadeconsole 0.1.0__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 (52) hide show
  1. kadeconsole-0.1.0/PKG-INFO +164 -0
  2. kadeconsole-0.1.0/README.md +123 -0
  3. kadeconsole-0.1.0/kadeconsole/__init__.py +10 -0
  4. kadeconsole-0.1.0/kadeconsole/cache/__init__.py +0 -0
  5. kadeconsole-0.1.0/kadeconsole/cache/store.py +155 -0
  6. kadeconsole-0.1.0/kadeconsole/cli.py +193 -0
  7. kadeconsole-0.1.0/kadeconsole/config/__init__.py +0 -0
  8. kadeconsole-0.1.0/kadeconsole/config/loader.py +111 -0
  9. kadeconsole-0.1.0/kadeconsole/core/__init__.py +0 -0
  10. kadeconsole-0.1.0/kadeconsole/core/parser.py +241 -0
  11. kadeconsole-0.1.0/kadeconsole/core/router.py +126 -0
  12. kadeconsole-0.1.0/kadeconsole/core/session.py +131 -0
  13. kadeconsole-0.1.0/kadeconsole/indicators/__init__.py +0 -0
  14. kadeconsole-0.1.0/kadeconsole/indicators/momentum.py +166 -0
  15. kadeconsole-0.1.0/kadeconsole/indicators/moving_averages.py +126 -0
  16. kadeconsole-0.1.0/kadeconsole/indicators/volatility.py +114 -0
  17. kadeconsole-0.1.0/kadeconsole/indicators/volume.py +79 -0
  18. kadeconsole-0.1.0/kadeconsole/jev/__init__.py +0 -0
  19. kadeconsole-0.1.0/kadeconsole/jev/client.py +140 -0
  20. kadeconsole-0.1.0/kadeconsole/jev/narratives.py +142 -0
  21. kadeconsole-0.1.0/kadeconsole/jev/ratings.py +253 -0
  22. kadeconsole-0.1.0/kadeconsole/jev/verdicts.py +199 -0
  23. kadeconsole-0.1.0/kadeconsole/pages/__init__.py +0 -0
  24. kadeconsole-0.1.0/kadeconsole/pages/des.py +97 -0
  25. kadeconsole-0.1.0/kadeconsole/pages/equity.py +248 -0
  26. kadeconsole-0.1.0/kadeconsole/pages/fa.py +212 -0
  27. kadeconsole-0.1.0/kadeconsole/pages/gp.py +88 -0
  28. kadeconsole-0.1.0/kadeconsole/pages/help.py +144 -0
  29. kadeconsole-0.1.0/kadeconsole/pages/hp.py +127 -0
  30. kadeconsole-0.1.0/kadeconsole/pages/rate.py +157 -0
  31. kadeconsole-0.1.0/kadeconsole/pages/verdict.py +167 -0
  32. kadeconsole-0.1.0/kadeconsole/providers/__init__.py +0 -0
  33. kadeconsole-0.1.0/kadeconsole/providers/base.py +139 -0
  34. kadeconsole-0.1.0/kadeconsole/providers/registry.py +102 -0
  35. kadeconsole-0.1.0/kadeconsole/providers/yfinance_provider.py +351 -0
  36. kadeconsole-0.1.0/kadeconsole/render/__init__.py +0 -0
  37. kadeconsole-0.1.0/kadeconsole/render/charts.py +144 -0
  38. kadeconsole-0.1.0/kadeconsole/render/tables.py +227 -0
  39. kadeconsole-0.1.0/kadeconsole/render/theme.py +105 -0
  40. kadeconsole-0.1.0/kadeconsole/tests/__init__.py +0 -0
  41. kadeconsole-0.1.0/kadeconsole.egg-info/PKG-INFO +164 -0
  42. kadeconsole-0.1.0/kadeconsole.egg-info/SOURCES.txt +50 -0
  43. kadeconsole-0.1.0/kadeconsole.egg-info/dependency_links.txt +1 -0
  44. kadeconsole-0.1.0/kadeconsole.egg-info/entry_points.txt +2 -0
  45. kadeconsole-0.1.0/kadeconsole.egg-info/requires.txt +22 -0
  46. kadeconsole-0.1.0/kadeconsole.egg-info/top_level.txt +1 -0
  47. kadeconsole-0.1.0/pyproject.toml +62 -0
  48. kadeconsole-0.1.0/setup.cfg +4 -0
  49. kadeconsole-0.1.0/tests/test_cache.py +82 -0
  50. kadeconsole-0.1.0/tests/test_indicators.py +186 -0
  51. kadeconsole-0.1.0/tests/test_parser.py +150 -0
  52. kadeconsole-0.1.0/tests/test_tables.py +101 -0
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: kadeconsole
3
+ Version: 0.1.0
4
+ Summary: Bloomberg-terminal-style equity research console powered by yfinance and Jev AI
5
+ Author: kadeConsole Contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/kade-console/kade-console
8
+ Project-URL: Repository, https://github.com/kade-console/kade-console
9
+ Project-URL: Issues, https://github.com/kade-console/kade-console/issues
10
+ Project-URL: Changelog, https://github.com/kade-console/kade-console/blob/main/CHANGELOG.md
11
+ Keywords: finance,terminal,equities,bloomberg,yfinance,ai
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Office/Business :: Financial :: Investment
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: yfinance>=0.2.40
24
+ Requires-Dist: rich>=13.7.0
25
+ Requires-Dist: prompt_toolkit>=3.0.43
26
+ Requires-Dist: plotext>=5.2.8
27
+ Requires-Dist: PyYAML>=6.0.1
28
+ Requires-Dist: pandas>=2.1.0
29
+ Requires-Dist: numpy>=1.26.0
30
+ Requires-Dist: requests>=2.31.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
33
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
34
+ Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
35
+ Provides-Extra: jev
36
+ Requires-Dist: typesafe-sdk>=0.1.0; extra == "jev"
37
+ Provides-Extra: alpaca
38
+ Requires-Dist: alpaca-trade-api>=3.0.0; extra == "alpaca"
39
+ Provides-Extra: alphavantage
40
+ Requires-Dist: alpha-vantage>=2.3.1; extra == "alphavantage"
41
+
42
+ # kadeConsole
43
+
44
+ > A Bloomberg-terminal-style equity research console — free, open-source, and AI-powered.
45
+
46
+ ```
47
+ ██╗ ██╗ █████╗ ██████╗ ███████╗
48
+ ██║ ██╔╝██╔══██╗██╔══██╗██╔════╝
49
+ █████╔╝ ███████║██║ ██║█████╗
50
+ ██╔═██╗ ██╔══██║██║ ██║██╔══╝
51
+ ██║ ██╗██║ ██║██████╔╝███████╗
52
+ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝ CONSOLE v0.1.0
53
+ ```
54
+
55
+ Bloomberg terminals cost ~$24k/year. kadeConsole is free. It gives you the muscle-memory command grammar of a real terminal, dense richly-formatted output, and **Jev** (TypeSafe AI) embedded in every screen — delivering the one non-obvious insight that raw numbers alone can't.
56
+
57
+ ---
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ pip install kadeconsole
63
+ ```
64
+
65
+ Then launch the REPL:
66
+
67
+ ```bash
68
+ kadeconsole
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Quickstart
74
+
75
+ ```
76
+ kadeConsole > AAPL EQUITY # resolve Apple, set session
77
+ kadeConsole > DES # company description
78
+ kadeConsole > FA # full fundamentals
79
+ kadeConsole > FA Q # quarterly fundamentals
80
+ kadeConsole > GP # 1Y price chart
81
+ kadeConsole > GP 6M # 6-month chart
82
+ kadeConsole > HP # historical OHLCV
83
+ kadeConsole > VERDICT # Jev buy/sell/hold distribution
84
+ kadeConsole > RATE # Jev multi-axis fundamental rating
85
+ kadeConsole > HELP # full command reference
86
+ kadeConsole > EXIT # quit
87
+ ```
88
+
89
+ **Indian markets:**
90
+ ```
91
+ kadeConsole > RELIANCE IN NS EQUITY # Reliance Industries on NSE
92
+ kadeConsole > INFY BO EQUITY # Infosys on BSE
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Command Reference
98
+
99
+ | Command | Description |
100
+ |---|---|
101
+ | `<TICKER> [EXCHANGE] EQUITY` | Resolve security, set session, show snapshot |
102
+ | `DES` | Company description, sector, key people |
103
+ | `FA` / `FA Q` | Full fundamentals (annual / quarterly) |
104
+ | `GP [timeframe]` | In-terminal price chart (`1D 5D 1M 3M 6M 1Y 2Y 5Y`) |
105
+ | `HP` | Historical OHLCV table |
106
+ | `VERDICT` | Jev buy/sell/hold probability distribution |
107
+ | `RATE` | Jev multi-axis rating (valuation/growth/quality/momentum) |
108
+ | `HELP` / `?` | Command reference |
109
+ | `EXIT` / `QUIT` | Exit the REPL |
110
+
111
+ *(Phase 2: TA, NI, COMPARE, SOURCE — Phase 3: SCREEN, MACRO, WATCH, EXPORT)*
112
+
113
+ ---
114
+
115
+ ## Configuration
116
+
117
+ On first run, kadeConsole creates `~/.kadeconsole/config.yaml`:
118
+
119
+ ```yaml
120
+ provider_priority:
121
+ - yfinance
122
+
123
+ typesafe_api_key: "" # Get yours at https://typesafe.ai
124
+ alpaca_api_key: ""
125
+ alphavantage_api_key: ""
126
+
127
+ default_timeframe: 1Y
128
+ theme: default
129
+ ```
130
+
131
+ Set `typesafe_api_key` to enable **Jev** AI analysis (VERDICT, RATE, anomaly flags). The terminal is fully functional without it — Jev sections show a graceful "unavailable" notice.
132
+
133
+ ---
134
+
135
+ ## Provider Plugins
136
+
137
+ The `providers/base.py` `DataProvider` interface is public. To add a new source:
138
+
139
+ 1. Subclass `DataProvider`
140
+ 2. Implement `get_info()`, `get_price_history()`, `get_financials()`, `get_news()`, `get_peers()`
141
+ 3. Register in `~/.kadeconsole/config.yaml` under `provider_priority`
142
+
143
+ ---
144
+
145
+ ## Requirements
146
+
147
+ - Python 3.10+
148
+ - A modern terminal: **iTerm2** (macOS), **Windows Terminal** (Windows), **GNOME Terminal** / **Kitty** (Linux)
149
+ - *Optional:* TypeSafe AI key for Jev features
150
+
151
+ ---
152
+
153
+ ## Roadmap
154
+
155
+ - **v0.1 (MVP):** EQUITY, DES, FA, GP, HP, VERDICT, RATE
156
+ - **v0.2:** TA (technical panel), NI (news + sentiment), COMPARE, multi-provider
157
+ - **v0.3:** SCREEN (NL filtering), MACRO, WATCH, EXPORT
158
+ - **v0.4:** Full Textual TUI, tabbed panels, community plugins
159
+
160
+ ---
161
+
162
+ ## License
163
+
164
+ MIT — use freely, contribute openly.
@@ -0,0 +1,123 @@
1
+ # kadeConsole
2
+
3
+ > A Bloomberg-terminal-style equity research console — free, open-source, and AI-powered.
4
+
5
+ ```
6
+ ██╗ ██╗ █████╗ ██████╗ ███████╗
7
+ ██║ ██╔╝██╔══██╗██╔══██╗██╔════╝
8
+ █████╔╝ ███████║██║ ██║█████╗
9
+ ██╔═██╗ ██╔══██║██║ ██║██╔══╝
10
+ ██║ ██╗██║ ██║██████╔╝███████╗
11
+ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝ CONSOLE v0.1.0
12
+ ```
13
+
14
+ Bloomberg terminals cost ~$24k/year. kadeConsole is free. It gives you the muscle-memory command grammar of a real terminal, dense richly-formatted output, and **Jev** (TypeSafe AI) embedded in every screen — delivering the one non-obvious insight that raw numbers alone can't.
15
+
16
+ ---
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ pip install kadeconsole
22
+ ```
23
+
24
+ Then launch the REPL:
25
+
26
+ ```bash
27
+ kadeconsole
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Quickstart
33
+
34
+ ```
35
+ kadeConsole > AAPL EQUITY # resolve Apple, set session
36
+ kadeConsole > DES # company description
37
+ kadeConsole > FA # full fundamentals
38
+ kadeConsole > FA Q # quarterly fundamentals
39
+ kadeConsole > GP # 1Y price chart
40
+ kadeConsole > GP 6M # 6-month chart
41
+ kadeConsole > HP # historical OHLCV
42
+ kadeConsole > VERDICT # Jev buy/sell/hold distribution
43
+ kadeConsole > RATE # Jev multi-axis fundamental rating
44
+ kadeConsole > HELP # full command reference
45
+ kadeConsole > EXIT # quit
46
+ ```
47
+
48
+ **Indian markets:**
49
+ ```
50
+ kadeConsole > RELIANCE IN NS EQUITY # Reliance Industries on NSE
51
+ kadeConsole > INFY BO EQUITY # Infosys on BSE
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Command Reference
57
+
58
+ | Command | Description |
59
+ |---|---|
60
+ | `<TICKER> [EXCHANGE] EQUITY` | Resolve security, set session, show snapshot |
61
+ | `DES` | Company description, sector, key people |
62
+ | `FA` / `FA Q` | Full fundamentals (annual / quarterly) |
63
+ | `GP [timeframe]` | In-terminal price chart (`1D 5D 1M 3M 6M 1Y 2Y 5Y`) |
64
+ | `HP` | Historical OHLCV table |
65
+ | `VERDICT` | Jev buy/sell/hold probability distribution |
66
+ | `RATE` | Jev multi-axis rating (valuation/growth/quality/momentum) |
67
+ | `HELP` / `?` | Command reference |
68
+ | `EXIT` / `QUIT` | Exit the REPL |
69
+
70
+ *(Phase 2: TA, NI, COMPARE, SOURCE — Phase 3: SCREEN, MACRO, WATCH, EXPORT)*
71
+
72
+ ---
73
+
74
+ ## Configuration
75
+
76
+ On first run, kadeConsole creates `~/.kadeconsole/config.yaml`:
77
+
78
+ ```yaml
79
+ provider_priority:
80
+ - yfinance
81
+
82
+ typesafe_api_key: "" # Get yours at https://typesafe.ai
83
+ alpaca_api_key: ""
84
+ alphavantage_api_key: ""
85
+
86
+ default_timeframe: 1Y
87
+ theme: default
88
+ ```
89
+
90
+ Set `typesafe_api_key` to enable **Jev** AI analysis (VERDICT, RATE, anomaly flags). The terminal is fully functional without it — Jev sections show a graceful "unavailable" notice.
91
+
92
+ ---
93
+
94
+ ## Provider Plugins
95
+
96
+ The `providers/base.py` `DataProvider` interface is public. To add a new source:
97
+
98
+ 1. Subclass `DataProvider`
99
+ 2. Implement `get_info()`, `get_price_history()`, `get_financials()`, `get_news()`, `get_peers()`
100
+ 3. Register in `~/.kadeconsole/config.yaml` under `provider_priority`
101
+
102
+ ---
103
+
104
+ ## Requirements
105
+
106
+ - Python 3.10+
107
+ - A modern terminal: **iTerm2** (macOS), **Windows Terminal** (Windows), **GNOME Terminal** / **Kitty** (Linux)
108
+ - *Optional:* TypeSafe AI key for Jev features
109
+
110
+ ---
111
+
112
+ ## Roadmap
113
+
114
+ - **v0.1 (MVP):** EQUITY, DES, FA, GP, HP, VERDICT, RATE
115
+ - **v0.2:** TA (technical panel), NI (news + sentiment), COMPARE, multi-provider
116
+ - **v0.3:** SCREEN (NL filtering), MACRO, WATCH, EXPORT
117
+ - **v0.4:** Full Textual TUI, tabbed panels, community plugins
118
+
119
+ ---
120
+
121
+ ## License
122
+
123
+ MIT — use freely, contribute openly.
@@ -0,0 +1,10 @@
1
+ """
2
+ kadeConsole — Bloomberg-terminal-style equity research console.
3
+
4
+ Usage:
5
+ pip install kadeconsole
6
+ kadeconsole
7
+ """
8
+
9
+ __version__ = "0.1.0"
10
+ __author__ = "kadeConsole Contributors"
File without changes
@@ -0,0 +1,155 @@
1
+ """
2
+ cache/store.py — SQLite-backed response and Jev-verdict cache.
3
+
4
+ Two tables:
5
+ provider_cache — raw market data, keyed by (symbol, endpoint), TTL 15 min
6
+ jev_cache — Jev responses, keyed by sha256 of (ticker+timeframe+data_hash), TTL 24h
7
+
8
+ The cache lives at ~/.kadeconsole/cache.db and is created automatically.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import hashlib
14
+ import json
15
+ import sqlite3
16
+ import time
17
+ from pathlib import Path
18
+ from typing import Any, Optional
19
+
20
+ CACHE_DB = Path.home() / ".kadeconsole" / "cache.db"
21
+
22
+ _CREATE_PROVIDER = """
23
+ CREATE TABLE IF NOT EXISTS provider_cache (
24
+ key TEXT PRIMARY KEY,
25
+ data_json TEXT NOT NULL,
26
+ expires_at REAL NOT NULL
27
+ );
28
+ """
29
+
30
+ _CREATE_JEV = """
31
+ CREATE TABLE IF NOT EXISTS jev_cache (
32
+ key TEXT PRIMARY KEY,
33
+ response_json TEXT NOT NULL,
34
+ created_at REAL NOT NULL,
35
+ expires_at REAL NOT NULL
36
+ );
37
+ """
38
+
39
+
40
+ class CacheStore:
41
+ """Thread-safe SQLite cache store."""
42
+
43
+ def __init__(
44
+ self,
45
+ db_path: Path = CACHE_DB,
46
+ provider_ttl_minutes: int = 15,
47
+ jev_ttl_hours: int = 24,
48
+ ) -> None:
49
+ self.db_path = db_path
50
+ self.provider_ttl = provider_ttl_minutes * 60
51
+ self.jev_ttl = jev_ttl_hours * 3600
52
+ self._ensure_db()
53
+
54
+ # ── Public API ─────────────────────────────────────────────────────────────
55
+
56
+ def get_provider(self, symbol: str, endpoint: str) -> Optional[Any]:
57
+ """Return cached provider data or None if missing/expired."""
58
+ key = self._provider_key(symbol, endpoint)
59
+ row = self._fetch("provider_cache", key)
60
+ return json.loads(row) if row else None
61
+
62
+ def set_provider(self, symbol: str, endpoint: str, data: Any) -> None:
63
+ """Cache provider data with TTL."""
64
+ key = self._provider_key(symbol, endpoint)
65
+ self._upsert("provider_cache", key, json.dumps(data, default=str), self.provider_ttl)
66
+
67
+ def get_jev(self, ticker: str, timeframe: str, data_fingerprint: str) -> Optional[Any]:
68
+ """Return cached Jev response or None if missing/expired."""
69
+ key = self._jev_key(ticker, timeframe, data_fingerprint)
70
+ row = self._fetch_jev(key)
71
+ return json.loads(row) if row else None
72
+
73
+ def set_jev(self, ticker: str, timeframe: str, data_fingerprint: str, response: Any) -> None:
74
+ """Cache a Jev response with TTL."""
75
+ key = self._jev_key(ticker, timeframe, data_fingerprint)
76
+ self._upsert("jev_cache", key, json.dumps(response, default=str), self.jev_ttl)
77
+
78
+ def clear_expired(self) -> None:
79
+ """Purge all expired rows from both tables."""
80
+ now = time.time()
81
+ with self._connect() as conn:
82
+ conn.execute("DELETE FROM provider_cache WHERE expires_at < ?", (now,))
83
+ conn.execute("DELETE FROM jev_cache WHERE expires_at < ?", (now,))
84
+
85
+ def clear_all(self) -> None:
86
+ """Nuke entire cache (useful for debugging)."""
87
+ with self._connect() as conn:
88
+ conn.execute("DELETE FROM provider_cache")
89
+ conn.execute("DELETE FROM jev_cache")
90
+
91
+ # ── Helpers ────────────────────────────────────────────────────────────────
92
+
93
+ @staticmethod
94
+ def make_data_fingerprint(data: Any) -> str:
95
+ """Return a short sha256 fingerprint of any JSON-serialisable data."""
96
+ raw = json.dumps(data, sort_keys=True, default=str).encode()
97
+ return hashlib.sha256(raw).hexdigest()[:16]
98
+
99
+ # ── Internal ───────────────────────────────────────────────────────────────
100
+
101
+ def _ensure_db(self) -> None:
102
+ self.db_path.parent.mkdir(parents=True, exist_ok=True)
103
+ with self._connect() as conn:
104
+ conn.execute(_CREATE_PROVIDER)
105
+ conn.execute(_CREATE_JEV)
106
+
107
+ def _connect(self) -> sqlite3.Connection:
108
+ return sqlite3.connect(str(self.db_path))
109
+
110
+ def _fetch(self, table: str, key: str) -> Optional[str]:
111
+ """Fetch from provider_cache (data_json column)."""
112
+ now = time.time()
113
+ with self._connect() as conn:
114
+ row = conn.execute(
115
+ f"SELECT data_json FROM {table} WHERE key=? AND expires_at>?",
116
+ (key, now),
117
+ ).fetchone()
118
+ return row[0] if row else None
119
+
120
+ def _fetch_jev(self, key: str) -> Optional[str]:
121
+ """Fetch from jev_cache (response_json column)."""
122
+ now = time.time()
123
+ with self._connect() as conn:
124
+ row = conn.execute(
125
+ "SELECT response_json FROM jev_cache WHERE key=? AND expires_at>?",
126
+ (key, now),
127
+ ).fetchone()
128
+ return row[0] if row else None
129
+
130
+ def _upsert(self, table: str, key: str, data_json: str, ttl: float) -> None:
131
+ now = time.time()
132
+ expires = now + ttl
133
+ if table == "provider_cache":
134
+ sql = (
135
+ "INSERT OR REPLACE INTO provider_cache (key, data_json, expires_at) "
136
+ "VALUES (?, ?, ?)"
137
+ )
138
+ with self._connect() as conn:
139
+ conn.execute(sql, (key, data_json, expires))
140
+ else:
141
+ sql = (
142
+ "INSERT OR REPLACE INTO jev_cache (key, response_json, created_at, expires_at) "
143
+ "VALUES (?, ?, ?, ?)"
144
+ )
145
+ with self._connect() as conn:
146
+ conn.execute(sql, (key, data_json, now, expires))
147
+
148
+ @staticmethod
149
+ def _provider_key(symbol: str, endpoint: str) -> str:
150
+ return f"{symbol.upper()}:{endpoint}"
151
+
152
+ @staticmethod
153
+ def _jev_key(ticker: str, timeframe: str, fingerprint: str) -> str:
154
+ raw = f"{ticker.upper()}:{timeframe}:{fingerprint}"
155
+ return hashlib.sha256(raw.encode()).hexdigest()
@@ -0,0 +1,193 @@
1
+ """
2
+ cli.py — kadeConsole REPL entrypoint.
3
+
4
+ Entry point: `kadeconsole` (via pyproject.toml console_scripts)
5
+
6
+ Startup sequence:
7
+ 1. Load config from ~/.kadeconsole/config.yaml
8
+ 2. Initialise provider registry + Jev client + SQLite cache
9
+ 3. Print startup banner
10
+ 4. Drop into prompt_toolkit REPL loop
11
+ 5. Parse each input line → Router.dispatch() → continue or exit
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import sys
17
+ from typing import Optional
18
+
19
+ from rich.console import Console
20
+
21
+ from kadeconsole.render.theme import RICH_THEME, t
22
+ from kadeconsole import __version__
23
+
24
+ console = Console(theme=RICH_THEME)
25
+
26
+ # ── Banner ─────────────────────────────────────────────────────────────────────
27
+ _BANNER = r"""
28
+ ██╗ ██╗ █████╗ ██████╗ ███████╗
29
+ ██║ ██╔╝██╔══██╗██╔══██╗██╔════╝
30
+ █████╔╝ ███████║██║ ██║█████╗
31
+ ██╔═██╗ ██╔══██║██║ ██║██╔══╝
32
+ ██║ ██╗██║ ██║██████╔╝███████╗
33
+ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝
34
+ """
35
+
36
+
37
+ def _print_banner() -> None:
38
+ console.print(f"[accent]{_BANNER}[/accent]", highlight=False)
39
+ console.print(
40
+ f" [bold {t.accent}]CONSOLE[/bold {t.accent}] "
41
+ f"[{t.muted}]v{__version__}[/] "
42
+ f"[{t.dim_text}]· Bloomberg-style equity research terminal[/]\n"
43
+ )
44
+ console.print(
45
+ f" [{t.muted}]Type [accent]HELP[/accent] or [accent]?[/accent] for the command reference. "
46
+ f"[accent]EXIT[/accent] or [accent]Q[/accent] to quit.[/]\n"
47
+ )
48
+
49
+
50
+ # ── Autocomplete word list ─────────────────────────────────────────────────────
51
+ _AUTOCOMPLETE_WORDS = [
52
+ "EQUITY", "DES", "FA", "FA Q", "GP", "HP", "VERDICT", "RATE",
53
+ "TA", "NI", "RV", "COMPARE", "MACRO", "SCREEN", "WATCH", "EXPORT",
54
+ "SOURCE", "HELP", "EXIT", "QUIT",
55
+ "1D", "5D", "1M", "3M", "6M", "1Y", "2Y", "5Y",
56
+ ]
57
+
58
+
59
+ def _build_completer():
60
+ """Build a prompt_toolkit WordCompleter for the REPL."""
61
+ try:
62
+ from prompt_toolkit.completion import WordCompleter
63
+ return WordCompleter(_AUTOCOMPLETE_WORDS, ignore_case=True, sentence=True)
64
+ except ImportError:
65
+ return None
66
+
67
+
68
+ def _build_prompt_session():
69
+ """Build a prompt_toolkit PromptSession (with history)."""
70
+ try:
71
+ from prompt_toolkit import PromptSession
72
+ from prompt_toolkit.history import FileHistory
73
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
74
+ from pathlib import Path
75
+
76
+ hist_file = Path.home() / ".kadeconsole" / "history"
77
+ hist_file.parent.mkdir(parents=True, exist_ok=True)
78
+
79
+ return PromptSession(
80
+ history=AutoSuggestFromHistory() and FileHistory(str(hist_file)),
81
+ auto_suggest=AutoSuggestFromHistory(),
82
+ completer=_build_completer(),
83
+ complete_while_typing=False,
84
+ )
85
+ except ImportError:
86
+ return None
87
+
88
+
89
+ def _simple_input(prompt: str) -> str:
90
+ """Fallback to built-in input() when prompt_toolkit is unavailable."""
91
+ try:
92
+ return input(prompt)
93
+ except EOFError:
94
+ return "EXIT"
95
+
96
+
97
+ def main() -> None:
98
+ """Main entrypoint — called by the `kadeconsole` console script."""
99
+
100
+ # ── 1. Load config ─────────────────────────────────────────────────────────
101
+ from kadeconsole.config.loader import load_config
102
+ config = load_config()
103
+
104
+ # ── 2. Initialise cache ────────────────────────────────────────────────────
105
+ from kadeconsole.cache.store import CacheStore
106
+ cache = CacheStore(
107
+ provider_ttl_minutes = config.get("cache_ttl_provider_minutes", 15),
108
+ jev_ttl_hours = config.get("cache_ttl_jev_hours", 24),
109
+ )
110
+ cache.clear_expired() # housekeeping on startup
111
+
112
+ # ── 3. Initialise providers ────────────────────────────────────────────────
113
+ from kadeconsole.providers.registry import ProviderRegistry
114
+ registry = ProviderRegistry(config)
115
+ provider = registry.get_primary()
116
+
117
+ # ── 4. Initialise Jev client ───────────────────────────────────────────────
118
+ from kadeconsole.jev.client import JevClient
119
+ jev = JevClient(
120
+ api_key = config.get("typesafe_api_key", ""),
121
+ cache = cache,
122
+ )
123
+
124
+ # ── 5. Session + Router ───────────────────────────────────────────────────
125
+ from kadeconsole.core.session import Session
126
+ from kadeconsole.core.router import Router
127
+ session = Session(timeframe=config.get("default_timeframe", "1Y"))
128
+ router = Router(session=session, provider=provider, jev=jev)
129
+
130
+ # ── 6. Banner ──────────────────────────────────────────────────────────────
131
+ _print_banner()
132
+
133
+ # Provider status line
134
+ provider_line = f"[{t.muted}] Data: [accent]{provider.name}[/accent]"
135
+ if registry.provider_names:
136
+ provider_line += f" [{t.muted}]({', '.join(registry.provider_names)})[/]"
137
+ jev_status = (
138
+ f"[positive]◆ Jev active[/positive]"
139
+ if jev.available
140
+ else f"[{t.muted}]◆ Jev inactive — set TYPESAFE_API_KEY to enable AI analysis[/]"
141
+ )
142
+ console.print(f"{provider_line} {jev_status}\n")
143
+
144
+ # ── 7. REPL loop ───────────────────────────────────────────────────────────
145
+ prompt_session = _build_prompt_session()
146
+
147
+ # Prompt style — amber "kadeConsole >" prefix
148
+ prompt_str = f"kadeConsole > "
149
+
150
+ while True:
151
+ try:
152
+ if prompt_session is not None:
153
+ try:
154
+ from prompt_toolkit.styles import Style as PTStyle
155
+ pt_style = PTStyle.from_dict({
156
+ "": f"#{t.text[1:]}",
157
+ "prompt": f"bold #{t.accent[1:]}",
158
+ })
159
+ raw = prompt_session.prompt(
160
+ [("class:prompt", prompt_str)],
161
+ style=pt_style,
162
+ )
163
+ except Exception:
164
+ raw = prompt_session.prompt(prompt_str)
165
+ else:
166
+ raw = _simple_input(prompt_str)
167
+
168
+ except KeyboardInterrupt:
169
+ console.print(f"\n[{t.muted}] Use EXIT or Q to quit.[/]\n")
170
+ continue
171
+ except EOFError:
172
+ break
173
+
174
+ raw = raw.strip()
175
+ if not raw:
176
+ continue
177
+
178
+ # Parse and dispatch
179
+ from kadeconsole.core.parser import parse, ParseError
180
+ try:
181
+ parsed = parse(raw)
182
+ except ParseError as exc:
183
+ console.print(f"[negative] Parse error: {exc}[/negative]")
184
+ continue
185
+
186
+ keep_going = router.dispatch(parsed)
187
+ if not keep_going:
188
+ console.print(f"\n[{t.muted}] Goodbye.[/]\n")
189
+ break
190
+
191
+
192
+ if __name__ == "__main__":
193
+ main()
File without changes