|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
3 |
| -import click |
4 | 3 | import asyncio
|
5 |
| -import pandas as pd |
6 | 4 | from typing import TYPE_CHECKING
|
| 5 | + |
| 6 | +import click |
| 7 | +import pandas as pd |
| 8 | +from asciichartpy import plot |
| 9 | +from ccy.cli.console import df_to_rich |
7 | 10 | from fluid.utils.data import compact_dict
|
8 | 11 | from fluid.utils.http_client import HttpResponseError
|
9 |
| -from ccy.cli.console import df_to_rich |
10 |
| -from quantflow.data.fmp import FMP |
11 |
| -from .stocks import from_context |
12 | 12 |
|
| 13 | +from quantflow.data.fred import Fred |
| 14 | + |
| 15 | +from .base import QuantContext, QuantGroup |
13 | 16 |
|
14 |
| -FREQUENCIES = tuple(FMP().historical_frequencies()) |
| 17 | +FREQUENCIES = tuple(Fred.freq) |
15 | 18 |
|
16 | 19 | if TYPE_CHECKING:
|
17 |
| - from quantflow.cli.app import QfApp |
| 20 | + pass |
18 | 21 |
|
19 | 22 |
|
20 |
| -@click.group(invoke_without_command=True) |
21 |
| -@click.pass_context |
22 |
| -def fred(ctx: click.Context) -> None: |
| 23 | +@click.group(invoke_without_command=True, cls=QuantGroup) |
| 24 | +def fred() -> None: |
23 | 25 | """Federal Reserve of St. Louis data"""
|
| 26 | + ctx = QuantContext.current() |
24 | 27 | if ctx.invoked_subcommand is None:
|
25 |
| - app = from_context(ctx) |
26 |
| - app.print("Welcome to FRED data commands!") |
27 |
| - app.print(ctx.get_help()) |
| 28 | + ctx.qf.print("Welcome to FRED data commands!") |
| 29 | + ctx.qf.print(ctx.get_help()) |
28 | 30 |
|
29 | 31 |
|
30 | 32 | @fred.command()
|
31 |
| -@click.pass_context |
32 | 33 | @click.argument("category-id", required=False)
|
33 |
| -def subcategories(ctx: click.Context, category_id: str | None = None) -> None: |
| 34 | +def subcategories(category_id: str | None = None) -> None: |
34 | 35 | """List subcategories for a Fred category"""
|
35 |
| - app = from_context(ctx) |
| 36 | + ctx = QuantContext.current() |
36 | 37 | try:
|
37 |
| - data = asyncio.run(get_subcategories(app, category_id)) |
| 38 | + data = asyncio.run(get_subcategories(ctx, category_id)) |
38 | 39 | except HttpResponseError as e:
|
39 |
| - app.error(e) |
| 40 | + ctx.qf.error(e) |
40 | 41 | else:
|
41 | 42 | df = pd.DataFrame(data["categories"], columns=["id", "name"])
|
42 |
| - app.print(df_to_rich(df)) |
| 43 | + ctx.qf.print(df_to_rich(df)) |
43 | 44 |
|
44 | 45 |
|
45 | 46 | @fred.command()
|
46 |
| -@click.pass_context |
47 | 47 | @click.argument("category-id")
|
48 |
| -def series(ctx: click.Context, category_id: str) -> None: |
| 48 | +def series(category_id: str) -> None: |
49 | 49 | """List series for a Fred category"""
|
50 |
| - app = from_context(ctx) |
| 50 | + ctx = QuantContext.current() |
51 | 51 | try:
|
52 |
| - data = asyncio.run(get_series(app, category_id)) |
| 52 | + data = asyncio.run(get_series(ctx, category_id)) |
53 | 53 | except HttpResponseError as e:
|
54 |
| - app.error(e) |
| 54 | + ctx.qf.error(e) |
55 | 55 | else:
|
56 |
| - app.print(data) |
| 56 | + ctx.qf.print(data) |
57 | 57 | # df = pd.DataFrame(data["categories"], columns=["id", "name"])
|
58 | 58 | # app.print(df_to_rich(df))
|
59 | 59 |
|
60 | 60 |
|
61 |
| -async def get_subcategories(app: QfApp, category_id: str | None) -> dict: |
62 |
| - async with app.fred() as cli: |
| 61 | +@fred.command() |
| 62 | +@click.argument("series-id") |
| 63 | +@click.option( |
| 64 | + "-l", |
| 65 | + "--length", |
| 66 | + type=int, |
| 67 | + default=100, |
| 68 | + show_default=True, |
| 69 | + help="Number of data points", |
| 70 | +) |
| 71 | +@click.option( |
| 72 | + "-f", |
| 73 | + "--frequency", |
| 74 | + type=click.Choice(FREQUENCIES), |
| 75 | + default="d", |
| 76 | + show_default=True, |
| 77 | + help="Frequency of data", |
| 78 | +) |
| 79 | +def data(series_id: str, length: int, frequency: str) -> None: |
| 80 | + """Display a series data""" |
| 81 | + ctx = QuantContext.current() |
| 82 | + try: |
| 83 | + df = asyncio.run(get_serie_data(ctx, series_id, length, frequency)) |
| 84 | + except HttpResponseError as e: |
| 85 | + ctx.qf.error(e) |
| 86 | + else: |
| 87 | + ctx.qf.print(df_to_rich(df)) |
| 88 | + |
| 89 | + |
| 90 | +@fred.command() |
| 91 | +@click.argument("series-id") |
| 92 | +@click.option( |
| 93 | + "-h", |
| 94 | + "--height", |
| 95 | + type=int, |
| 96 | + default=20, |
| 97 | + show_default=True, |
| 98 | + help="Chart height", |
| 99 | +) |
| 100 | +@click.option( |
| 101 | + "-l", |
| 102 | + "--length", |
| 103 | + type=int, |
| 104 | + default=100, |
| 105 | + show_default=True, |
| 106 | + help="Number of data points", |
| 107 | +) |
| 108 | +@click.option( |
| 109 | + "-f", |
| 110 | + "--frequency", |
| 111 | + type=click.Choice(FREQUENCIES), |
| 112 | + default="w", |
| 113 | + show_default=True, |
| 114 | + help="Frequency of data", |
| 115 | +) |
| 116 | +def chart(series_id: str, height: int, length: int, frequency: str) -> None: |
| 117 | + """Chart a serie""" |
| 118 | + ctx = QuantContext.current() |
| 119 | + try: |
| 120 | + df = asyncio.run(get_serie_data(ctx, series_id, length, frequency)) |
| 121 | + except HttpResponseError as e: |
| 122 | + ctx.qf.error(e) |
| 123 | + else: |
| 124 | + data = list(reversed(df["value"].tolist()[:length])) |
| 125 | + ctx.qf.print(plot(data, {"height": height})) |
| 126 | + |
| 127 | + |
| 128 | +async def get_subcategories(ctx: QuantContext, category_id: str | None) -> dict: |
| 129 | + async with ctx.fred() as cli: |
63 | 130 | return await cli.subcategories(params=compact_dict(category_id=category_id))
|
64 | 131 |
|
65 | 132 |
|
66 |
| -async def get_series(app: QfApp, category_id: str) -> dict: |
67 |
| - async with app.fred() as cli: |
| 133 | +async def get_series(ctx: QuantContext, category_id: str) -> dict: |
| 134 | + async with ctx.fred() as cli: |
68 | 135 | return await cli.series(params=compact_dict(category_id=category_id))
|
| 136 | + |
| 137 | + |
| 138 | +async def get_serie_data( |
| 139 | + ctx: QuantContext, series_id: str, length: int, frequency: str |
| 140 | +) -> dict: |
| 141 | + async with ctx.fred() as cli: |
| 142 | + return await cli.serie_data( |
| 143 | + params=dict( |
| 144 | + series_id=series_id, |
| 145 | + limit=length, |
| 146 | + frequency=frequency, |
| 147 | + sort_order="desc", |
| 148 | + ) |
| 149 | + ) |
0 commit comments