|
1 | | -import asyncio |
2 | 1 | import os |
3 | 2 | from dataclasses import dataclass, field |
4 | | -from typing import Any, Self |
| 3 | +from typing import Any |
5 | 4 |
|
6 | 5 | import click |
7 | | -import dotenv |
8 | | -import pandas as pd |
9 | | -from asciichartpy import plot |
10 | | -from ccy.cli.console import df_to_rich |
11 | 6 | from prompt_toolkit import PromptSession |
12 | 7 | from prompt_toolkit.history import FileHistory |
13 | 8 | from rich.console import Console |
14 | 9 | from rich.text import Text |
15 | 10 |
|
16 | | -from quantflow.data.fmp import FMP |
17 | 11 |
|
18 | | -from . import settings |
19 | | - |
20 | | -dotenv.load_dotenv() |
21 | | - |
22 | | -FREQUENCIES = tuple(FMP().historical_frequencies()) |
| 12 | +from . import settings, commands |
23 | 13 |
|
24 | 14 |
|
25 | 15 | @click.group() |
26 | 16 | def qf() -> None: |
27 | 17 | pass |
28 | 18 |
|
29 | 19 |
|
30 | | -@qf.command() |
31 | | -@click.argument("symbol") |
32 | | -@click.pass_context |
33 | | -def profile(ctx: click.Context, symbol: str) -> None: |
34 | | - """Company profile""" |
35 | | - app = QfApp.from_context(ctx) |
36 | | - data = asyncio.run(get_profile(symbol))[0] |
37 | | - app.print(data.pop("description")) |
38 | | - df = pd.DataFrame(data.items(), columns=["Key", "Value"]) |
39 | | - app.print(df_to_rich(df)) |
40 | | - |
41 | | - |
42 | | -@qf.command() |
43 | | -@click.argument("symbol") |
44 | | -@click.option( |
45 | | - "-h", |
46 | | - "--height", |
47 | | - type=int, |
48 | | - default=20, |
49 | | - show_default=True, |
50 | | - help="Chart height", |
51 | | -) |
52 | | -@click.option( |
53 | | - "-l", |
54 | | - "--length", |
55 | | - type=int, |
56 | | - default=100, |
57 | | - show_default=True, |
58 | | - help="Number of data points", |
59 | | -) |
60 | | -@click.option( |
61 | | - "-f", |
62 | | - "--frequency", |
63 | | - type=click.Choice(FREQUENCIES), |
64 | | - default="", |
65 | | - help="Number of data points", |
66 | | -) |
67 | | -def chart(symbol: str, height: int, length: int, frequency: str) -> None: |
68 | | - """Symbol chart""" |
69 | | - df = asyncio.run(get_prices(symbol, frequency)) |
70 | | - data = list(reversed(df["close"].tolist()[:length])) |
71 | | - print(plot(data, {"height": height})) |
72 | | - |
73 | | - |
74 | | -async def get_prices(symbol: str, frequency: str) -> pd.DataFrame: |
75 | | - async with FMP() as cli: |
76 | | - return await cli.prices(symbol, frequency) |
77 | | - |
78 | | - |
79 | | -async def get_profile(symbol: str) -> list[dict]: |
80 | | - async with FMP() as cli: |
81 | | - return await cli.profile(symbol) |
| 20 | +qf.add_command(commands.exit) |
| 21 | +qf.add_command(commands.profile) |
| 22 | +qf.add_command(commands.search) |
| 23 | +qf.add_command(commands.chart) |
82 | 24 |
|
83 | 25 |
|
84 | 26 | @dataclass |
85 | 27 | class QfApp: |
86 | 28 | console: Console = field(default_factory=Console) |
87 | 29 |
|
88 | | - @classmethod |
89 | | - def from_context(cls, ctx: click.Context) -> Self: |
90 | | - return ctx.obj # type: ignore |
91 | | - |
92 | 30 | def __call__(self) -> None: |
93 | 31 | os.makedirs(settings.SETTINGS_DIRECTORY, exist_ok=True) |
94 | 32 | history = FileHistory(str(settings.HIST_FILE_PATH)) |
@@ -123,12 +61,12 @@ def handle_command(self, text: str) -> None: |
123 | 61 | return |
124 | 62 | elif text == "help": |
125 | 63 | return qf.main(["--help"], standalone_mode=False, obj=self) |
126 | | - elif text == "exit": |
127 | | - raise click.Abort() |
128 | 64 |
|
129 | 65 | try: |
130 | 66 | qf.main(text.split(), standalone_mode=False, obj=self) |
131 | 67 | except click.exceptions.MissingParameter as e: |
132 | 68 | self.error(e) |
133 | 69 | except click.exceptions.NoSuchOption as e: |
134 | 70 | self.error(e) |
| 71 | + except click.exceptions.UsageError as e: |
| 72 | + self.error(e) |
0 commit comments