qgist-cli 0.1.0__py3-none-any.whl

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.
qgist/__init__.py ADDED
File without changes
qgist/api.py ADDED
@@ -0,0 +1,21 @@
1
+ import os
2
+ from functools import lru_cache
3
+
4
+ from quantgist import QuantGistClient
5
+
6
+ from qgist.config_store import load_config
7
+
8
+
9
+ @lru_cache(maxsize=1)
10
+ def get_client() -> QuantGistClient:
11
+ cfg = load_config()
12
+ api_key = os.environ.get("QUANTGIST_API_KEY") or cfg.get("api_key", "")
13
+ if not api_key:
14
+ import typer
15
+ typer.echo(
16
+ "No API key found. Run: qgist config set api-key <your-key>\n"
17
+ "Or set QUANTGIST_API_KEY environment variable.",
18
+ err=True,
19
+ )
20
+ raise typer.Exit(1)
21
+ return QuantGistClient(api_key=api_key)
qgist/cli.py ADDED
@@ -0,0 +1,19 @@
1
+ import typer
2
+
3
+ app = typer.Typer(
4
+ name="qgist",
5
+ help="QuantGist CLI — macro event data in your terminal.",
6
+ add_completion=False,
7
+ rich_markup_mode="rich",
8
+ )
9
+
10
+ # Import commands so they register themselves on `app`
11
+ from qgist.commands import config, events, export, status # noqa: E402, F401
12
+
13
+
14
+ def main() -> None:
15
+ app()
16
+
17
+
18
+ if __name__ == "__main__":
19
+ main()
File without changes
@@ -0,0 +1,52 @@
1
+ """qgist config set <key> <value> | qgist config get <key> | qgist config show"""
2
+ from typing import Optional
3
+
4
+ import typer
5
+ from rich.console import Console
6
+ from rich.table import Table
7
+
8
+ from qgist.cli import app
9
+ from qgist.config_store import get_value, load_config, set_value
10
+
11
+ console = Console()
12
+
13
+ config_app = typer.Typer(help="Manage qgist configuration.")
14
+ app.add_typer(config_app, name="config")
15
+
16
+
17
+ @config_app.command("set")
18
+ def config_set(
19
+ key: str = typer.Argument(..., help="Config key (e.g. api-key, timezone)"),
20
+ value: str = typer.Argument(..., help="Value to set"),
21
+ ) -> None:
22
+ """Set a config value."""
23
+ set_value(key, value)
24
+ console.print(f"[green]✓[/] Set [bold]{key}[/] = {value!r}")
25
+
26
+
27
+ @config_app.command("get")
28
+ def config_get(
29
+ key: str = typer.Argument(..., help="Config key to retrieve"),
30
+ ) -> None:
31
+ """Get a single config value."""
32
+ val = get_value(key)
33
+ if val is None:
34
+ console.print(f"[yellow]Not set:[/] {key}")
35
+ raise typer.Exit(1)
36
+ console.print(val)
37
+
38
+
39
+ @config_app.command("show")
40
+ def config_show() -> None:
41
+ """Show all current config values."""
42
+ cfg = load_config()
43
+ if not cfg:
44
+ console.print("[dim]No config found. Run: qgist config set api-key <your-key>[/]")
45
+ return
46
+ table = Table(title="qgist config", show_header=True)
47
+ table.add_column("Key", style="cyan")
48
+ table.add_column("Value")
49
+ for k, v in cfg.items():
50
+ display = "****" + str(v)[-4:] if "key" in k else str(v)
51
+ table.add_row(k, display)
52
+ console.print(table)
@@ -0,0 +1,64 @@
1
+ """qgist events [today|week] [--symbol] [--country] [--impact] [--json]"""
2
+ import json
3
+ from datetime import date, timedelta
4
+ from typing import Optional
5
+
6
+ import typer
7
+ from rich.console import Console
8
+ from rich.table import Table
9
+
10
+ from qgist.cli import app
11
+ from qgist.api import get_client
12
+
13
+ console = Console()
14
+
15
+
16
+ @app.command("events")
17
+ def events_cmd(
18
+ period: str = typer.Argument("today", help="today | week"),
19
+ symbol: Optional[str] = typer.Option(None, "--symbol", "-s", help="Filter by symbol, e.g. XAUUSD"),
20
+ country: Optional[str] = typer.Option(None, "--country", "-c", help="ISO country code, e.g. us"),
21
+ impact: Optional[str] = typer.Option(None, "--impact", "-i", help="low | medium | high"),
22
+ as_json: bool = typer.Option(False, "--json", help="Output raw JSON"),
23
+ ) -> None:
24
+ today = date.today()
25
+ from_date = str(today)
26
+ to_date = str(today if period == "today" else today + timedelta(days=6))
27
+
28
+ client = get_client()
29
+ response = client.get_events(
30
+ from_date=from_date,
31
+ to_date=to_date,
32
+ symbol=symbol,
33
+ country=country,
34
+ impact=impact, # type: ignore[arg-type]
35
+ )
36
+
37
+ if as_json:
38
+ typer.echo(json.dumps([e.model_dump() for e in response.data], default=str))
39
+ return
40
+
41
+ table = Table(title=f"Events — {period}", show_lines=False)
42
+ table.add_column("Time (UTC)", style="cyan", no_wrap=True)
43
+ table.add_column("Impact", justify="center")
44
+ table.add_column("Country", style="dim")
45
+ table.add_column("Event")
46
+ table.add_column("Forecast", justify="right", style="dim")
47
+ table.add_column("Previous", justify="right", style="dim")
48
+ table.add_column("Actual", justify="right")
49
+
50
+ impact_colors = {"high": "[red]", "medium": "[yellow]", "low": "[green]"}
51
+
52
+ for event in response.data:
53
+ color = impact_colors.get(event.impact, "")
54
+ table.add_row(
55
+ event.release_time.strftime("%H:%M"),
56
+ f"{color}{(event.impact or '').upper()}[/]",
57
+ (event.country or "").upper(),
58
+ event.title,
59
+ str(event.forecast) if event.forecast is not None else "—",
60
+ str(event.previous) if event.previous is not None else "—",
61
+ str(event.actual) if event.actual is not None else "—",
62
+ )
63
+
64
+ console.print(table)
@@ -0,0 +1,70 @@
1
+ """qgist export <event-name> --from <date> --to <date> [--format csv|json]"""
2
+ import csv
3
+ import io
4
+ import json
5
+ import sys
6
+ from datetime import date
7
+ from typing import Optional
8
+
9
+ import typer
10
+ from rich.console import Console
11
+
12
+ from qgist.cli import app
13
+ from qgist.api import get_client
14
+
15
+ console = Console()
16
+
17
+
18
+ @app.command("export")
19
+ def export_cmd(
20
+ event_name: str = typer.Argument(..., help="Event keyword to filter (e.g. nfp, cpi)"),
21
+ from_date: Optional[str] = typer.Option(None, "--from", help="Start date YYYY-MM-DD"),
22
+ to_date: Optional[str] = typer.Option(None, "--to", help="End date YYYY-MM-DD"),
23
+ fmt: str = typer.Option("csv", "--format", "-f", help="Output format: csv | json"),
24
+ output: Optional[str] = typer.Option(None, "--output", "-o", help="Output file path (default: stdout)"),
25
+ ) -> None:
26
+ """Export historical event data to CSV or JSON."""
27
+ client = get_client()
28
+
29
+ # The API caps per_page at 100; pagination is not yet implemented in v0.1.
30
+ response = client.get_events(
31
+ from_date=from_date or "2020-01-01",
32
+ to_date=to_date or str(date.today()),
33
+ limit=100,
34
+ )
35
+
36
+ # filter by keyword in title
37
+ keyword = event_name.lower()
38
+ filtered = [e for e in response.data if keyword in e.title.lower()]
39
+
40
+ if not filtered:
41
+ Console(stderr=True).print(f"[yellow]No events found matching '{event_name}'[/]")
42
+ raise typer.Exit(1)
43
+
44
+ out = sys.stdout if not output else open(output, "w", newline="")
45
+
46
+ try:
47
+ if fmt == "json":
48
+ json.dump([e.model_dump() for e in filtered], out, default=str, indent=2)
49
+ if output:
50
+ console.print(f"[green]✓[/] Exported {len(filtered)} events to {output}")
51
+ else:
52
+ writer = csv.DictWriter(
53
+ out,
54
+ fieldnames=[
55
+ "id", "release_time", "title", "country", "currency",
56
+ "impact", "forecast", "previous", "actual", "surprise_score",
57
+ "symbols", "event_type", "source",
58
+ ],
59
+ extrasaction="ignore", # model has more fields than we export
60
+ )
61
+ writer.writeheader()
62
+ for e in filtered:
63
+ row = e.model_dump()
64
+ row["symbols"] = ",".join(row.get("symbols") or [])
65
+ writer.writerow(row)
66
+ if output:
67
+ console.print(f"[green]✓[/] Exported {len(filtered)} events to {output}")
68
+ finally:
69
+ if output:
70
+ out.close()
@@ -0,0 +1,44 @@
1
+ """qgist status — show API health, rate limit, and plan info"""
2
+ import typer
3
+ from rich.console import Console
4
+ from rich.panel import Panel
5
+ from rich.table import Table
6
+
7
+ from qgist.cli import app
8
+ from qgist.api import get_client
9
+ from qgist.config_store import get_value
10
+
11
+ console = Console()
12
+
13
+
14
+ @app.command("status")
15
+ def status_cmd() -> None:
16
+ """Show API connection status, rate limit, and plan."""
17
+ client = get_client()
18
+
19
+ console.print("[dim]Checking QuantGist API...[/]")
20
+ try:
21
+ resp = client.get_events(limit=1)
22
+ except Exception as e:
23
+ console.print(f"[red]✗ API error:[/] {e}")
24
+ raise typer.Exit(1)
25
+
26
+ table = Table(show_header=False, box=None, padding=(0, 2))
27
+ table.add_column("Key", style="dim")
28
+ table.add_column("Value", style="bold")
29
+
30
+ table.add_row("Status", "[green]✓ Connected[/]")
31
+
32
+ remaining = resp.meta.rate_limit_remaining
33
+ if remaining is not None:
34
+ color = "green" if remaining > 10 else "yellow" if remaining > 0 else "red"
35
+ table.add_row("Rate limit remaining", f"[{color}]{remaining}[/]")
36
+
37
+ api_key = get_value("api_key") or ""
38
+ if api_key:
39
+ masked = api_key[:12] + "****"
40
+ table.add_row("API key", masked)
41
+
42
+ table.add_row("API base", "https://api.quantgist.com/v1")
43
+
44
+ console.print(Panel(table, title="qgist status", border_style="cyan"))
qgist/config_store.py ADDED
@@ -0,0 +1,28 @@
1
+ import tomllib
2
+ import tomli_w
3
+ from pathlib import Path
4
+
5
+ _CONFIG_PATH = Path.home() / ".config" / "qgist" / "config.toml"
6
+
7
+
8
+ def load_config() -> dict:
9
+ if not _CONFIG_PATH.exists():
10
+ return {}
11
+ with open(_CONFIG_PATH, "rb") as f:
12
+ return tomllib.load(f)
13
+
14
+
15
+ def save_config(config: dict) -> None:
16
+ _CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
17
+ with open(_CONFIG_PATH, "wb") as f:
18
+ tomli_w.dump(config, f)
19
+
20
+
21
+ def set_value(key: str, value: str) -> None:
22
+ cfg = load_config()
23
+ cfg[key.replace("-", "_")] = value
24
+ save_config(cfg)
25
+
26
+
27
+ def get_value(key: str) -> str | None:
28
+ return load_config().get(key.replace("-", "_"))
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: qgist-cli
3
+ Version: 0.1.0
4
+ Summary: CLI for the QuantGist macro event API — qgist events today
5
+ Project-URL: Homepage, https://quantgist.com
6
+ Project-URL: Repository, https://github.com/QuantGist-Technologies/quantgist-cli
7
+ Project-URL: Documentation, https://quantgist.com/docs
8
+ Author-email: QuantGist <dev@quantgist.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: cli,earnings,economic-calendar,macro,quantgist,trading
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: quantgist>=0.7.1
14
+ Requires-Dist: rich>=13.0.0
15
+ Requires-Dist: tomli-w>=1.0.0
16
+ Requires-Dist: typer>=0.12.0
17
+ Description-Content-Type: text/markdown
18
+
19
+ # qgist
20
+
21
+ [![PyPI](https://img.shields.io/pypi/v/qgist-cli)](https://pypi.org/project/qgist-cli)
22
+ [![Python](https://img.shields.io/pypi/pyversions/qgist-cli)](https://pypi.org/project/qgist-cli)
23
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
24
+
25
+ Command-line interface for the [QuantGist](https://quantgist.com) macro event API. Check upcoming economic events, export historical data, and configure your key — all from the terminal.
26
+
27
+ The PyPI package is **`qgist-cli`**; it installs the **`qgist`** command.
28
+
29
+ ---
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install qgist-cli
35
+ # or (recommended — installs as an isolated tool)
36
+ uv tool install qgist-cli
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Quick start
42
+
43
+ ```bash
44
+ # 1. Set your API key (get one free at quantgist.com/signup)
45
+ qgist config set api-key qg_live_your_key_here
46
+
47
+ # 2. Check today's events
48
+ qgist events
49
+
50
+ # 3. Check this week's high-impact events
51
+ qgist events week --impact high
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Commands
57
+
58
+ ### `qgist events [today|week]`
59
+
60
+ Show upcoming macro events in a formatted table.
61
+
62
+ ```
63
+ Usage: qgist events [PERIOD] [OPTIONS]
64
+
65
+ Arguments:
66
+ PERIOD today (default) | week
67
+
68
+ Options:
69
+ -s, --symbol TEXT Filter by symbol (e.g. XAUUSD)
70
+ -c, --country TEXT ISO country code (e.g. us, eu, gb)
71
+ -i, --impact TEXT Impact filter: low | medium | high
72
+ --json Output raw JSON
73
+ ```
74
+
75
+ **Examples:**
76
+ ```bash
77
+ qgist events # Today, all impacts
78
+ qgist events week # Next 7 days
79
+ qgist events week --impact high # This week, high-impact only
80
+ qgist events today --country us # Today, US events
81
+ qgist events today --symbol XAUUSD # Today, Gold-affecting events
82
+ qgist events today --json # Raw JSON output
83
+
84
+ # Example output:
85
+ # ┌──────────────┬────────┬─────────┬──────────────────────────────┬──────────┬──────────┬────────┐
86
+ # │ Time (UTC) │ Impact │ Country │ Event │ Forecast │ Previous │ Actual │
87
+ # ├──────────────┼────────┼─────────┼──────────────────────────────┼──────────┼──────────┼────────┤
88
+ # │ 08:30 │ HIGH │ US │ Non-Farm Payrolls │ 185K │ 175K │ — │
89
+ # │ 10:00 │ HIGH │ US │ ISM Manufacturing PMI │ 48.5 │ 47.8 │ — │
90
+ # └──────────────┴────────┴─────────┴──────────────────────────────┴──────────┴──────────┘
91
+ ```
92
+
93
+ ---
94
+
95
+ ### `qgist config set|get|show`
96
+
97
+ Manage your API key and settings stored at `~/.config/qgist/config.toml`.
98
+
99
+ ```
100
+ Usage: qgist config set KEY VALUE
101
+ qgist config get KEY
102
+ qgist config show
103
+ ```
104
+
105
+ **Examples:**
106
+ ```bash
107
+ qgist config set api-key qg_live_abc123
108
+ qgist config get api-key
109
+ # api-key = qg_live_abc*****
110
+
111
+ qgist config show
112
+ # ╭── QuantGist Config ──────────────────╮
113
+ # │ api-key = qg_live_abc***** │
114
+ # │ base-url = https://api.quantgist.com │
115
+ # ╰──────────────────────────────────────╯
116
+ ```
117
+
118
+ Config file location: `~/.config/qgist/config.toml`
119
+
120
+ ---
121
+
122
+ ### `qgist export <keyword>`
123
+
124
+ Export historical events matching a keyword to CSV or JSON.
125
+
126
+ ```
127
+ Usage: qgist export KEYWORD [OPTIONS]
128
+
129
+ Arguments:
130
+ KEYWORD Keyword to search (e.g. nfp, cpi, fomc)
131
+
132
+ Options:
133
+ --from TEXT Start date (YYYY-MM-DD), default 30 days ago
134
+ --to TEXT End date (YYYY-MM-DD), default today
135
+ --format TEXT csv (default) | json
136
+ --output TEXT Output file path (default: stdout)
137
+ ```
138
+
139
+ **Examples:**
140
+ ```bash
141
+ qgist export nfp # NFP events, last 30 days, CSV to stdout
142
+ qgist export cpi --from 2024-01-01 --to 2024-12-31 --format json
143
+ qgist export fomc --output fomc_history.csv
144
+ ```
145
+
146
+ ---
147
+
148
+ ### `qgist status`
149
+
150
+ Check your connection and API key status.
151
+
152
+ ```bash
153
+ qgist status
154
+
155
+ # ╭─ QuantGist Status ──────────────────────────────╮
156
+ # │ Connection ✓ Connected │
157
+ # │ API Key qg_live_abc***** (active) │
158
+ # │ Plan free │
159
+ # │ Rate limit 47 / 100 remaining today │
160
+ # ╰─────────────────────────────────────────────────╯
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Configuration
166
+
167
+ The CLI stores settings in `~/.config/qgist/config.toml`:
168
+
169
+ ```toml
170
+ api-key = "qg_live_..."
171
+ base-url = "https://api.quantgist.com/v1"
172
+ ```
173
+
174
+ **Environment variable override** — set `QUANTGIST_API_KEY` to bypass the config file:
175
+ ```bash
176
+ export QUANTGIST_API_KEY=qg_live_...
177
+ qgist events
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Free API key
183
+
184
+ [quantgist.com/signup](https://quantgist.com/signup) — 100 calls/day, 365-day history. No credit card.
185
+
186
+ ---
187
+
188
+ ## License
189
+
190
+ MIT
@@ -0,0 +1,14 @@
1
+ qgist/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ qgist/api.py,sha256=Bf0ncFxgXA5WkC9v48ylrIrKwW1w_7lQY8z2IEm6kH4,588
3
+ qgist/cli.py,sha256=9K0Ve9LyA57hPPcH2AEqdlEqGE2IHwgnyWYzumDvxqo,379
4
+ qgist/config_store.py,sha256=5O4mgJk8y8h79OqYB2s_nazoSy1j4sXxIxm18bslGM4,669
5
+ qgist/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ qgist/commands/config.py,sha256=e-mMET-VONEmPX8Qg5YtZQNCjwluq9ZwO0iGTxWJjmQ,1556
7
+ qgist/commands/events.py,sha256=7QTPloOshYqxR8NFAOX5vAPsGm4iCOqaPn5LZGz7Z9A,2308
8
+ qgist/commands/export.py,sha256=ha8szVnPWg7O4DtqFTUdl8Imdt_BDEzHhBdbxm81FDM,2519
9
+ qgist/commands/status.py,sha256=vDS6LTPF7Mf07RDaaIo4bllZwZIzAs-A-KPN-uiMZTw,1362
10
+ qgist_cli-0.1.0.dist-info/METADATA,sha256=g-HtCBx96--FsUCTfbMSDdUiaQoSzN-0AfAsNgK7tC4,6008
11
+ qgist_cli-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
12
+ qgist_cli-0.1.0.dist-info/entry_points.txt,sha256=dmL648eL2no9wz_5ANGcrRqurHkAC1FB8ut8yEJDwUI,41
13
+ qgist_cli-0.1.0.dist-info/licenses/LICENSE,sha256=fnAhhCfxL11KcIyOszKs2Irs_pdw2kLSA9R-REOEaRk,1066
14
+ qgist_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ qgist = qgist.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 QuantGist
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.