Coverage for src/finbot/cli/main.py: 100%
54 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 17:12 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 17:12 +0000
1import argparse
2import logging
3import sys
4from typing import Sequence
6from finbot.cli.backtest import add_arguments as add_backtest_arguments
7from finbot.cli.backtest import add_compare_arguments
8from finbot.cli.backtest import run as run_backtest
9from finbot.cli.backtest import run_compare
10from finbot.logger import configure_logging, get_logger
12logger = get_logger(__name__)
15def _test_api(args: argparse.Namespace) -> int:
16 from finbot.config import load_settings
17 from finbot.market.alpaca import AlpacaMarketDataProvider
19 settings = load_settings(api_key=args.api_key, api_secret=args.api_secret)
20 market_data = AlpacaMarketDataProvider(
21 api_key=settings.alpaca_api_key,
22 api_secret=settings.alpaca_api_secret,
23 feed=settings.alpaca_feed,
24 )
26 bar = market_data.get_latest_bar("NVDA")
27 logger.info(bar)
29 return 0
32def _add_verbose_argument(
33 parser: argparse.ArgumentParser,
34 *,
35 suppress_default: bool = False,
36) -> None:
37 kwargs = {}
38 if suppress_default:
39 kwargs["default"] = argparse.SUPPRESS
41 parser.add_argument(
42 "-v",
43 "--verbose",
44 action="store_true",
45 help="Enable debug logging",
46 **kwargs,
47 )
50def _build_parser() -> argparse.ArgumentParser:
51 parser = argparse.ArgumentParser(description="FinBot CLI")
52 _add_verbose_argument(parser)
53 subparsers = parser.add_subparsers(dest="command", required=True)
55 test_api_parser = subparsers.add_parser(
56 "test_api",
57 help="Test the configured market data API connection",
58 )
59 _add_verbose_argument(test_api_parser, suppress_default=True)
60 test_api_parser.add_argument(
61 "--api-key",
62 "--api_key",
63 dest="api_key",
64 default=None,
65 help="API key for the market data provider",
66 )
67 test_api_parser.add_argument(
68 "--api-secret",
69 "--api_secret",
70 dest="api_secret",
71 default=None,
72 help="API secret for the market data provider",
73 )
75 test_api_parser.set_defaults(func=_test_api)
77 backtest_parser = subparsers.add_parser(
78 "backtest",
79 help="Run or inspect reproducible backtest experiments",
80 )
81 _add_verbose_argument(backtest_parser, suppress_default=True)
82 add_backtest_arguments(backtest_parser)
83 backtest_parser.set_defaults(func=run_backtest)
85 backtest_subparsers = backtest_parser.add_subparsers(dest="backtest_command")
87 compare_parser = backtest_subparsers.add_parser(
88 "compare",
89 help="Compare stored backtest experiments",
90 )
91 _add_verbose_argument(compare_parser, suppress_default=True)
92 add_compare_arguments(compare_parser)
93 compare_parser.set_defaults(func=run_compare)
95 return parser
98def main(argv: Sequence[str] | None = None) -> int:
99 parser = _build_parser()
100 args = parser.parse_args(argv)
102 level = logging.DEBUG if args.verbose else logging.INFO
103 configure_logging(level)
105 try:
106 return args.func(args)
107 except (ValueError, RuntimeError) as exc:
108 if args.verbose:
109 logger.exception("%s", exc)
110 else:
111 logger.error("%s", exc)
112 return 1
115if __name__ == "__main__":
116 sys.exit(main())