aipa-cli 0.1.43__tar.gz → 0.1.45__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 (49) hide show
  1. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/CHANGELOG.md +19 -0
  2. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/PKG-INFO +2 -2
  3. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/pyproject.toml +1 -1
  4. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/__init__.py +1 -1
  5. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/cli.py +5 -0
  6. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/cli_commands.py +37 -5
  7. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/.gitignore +0 -0
  8. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/LICENSE +0 -0
  9. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/README.md +0 -0
  10. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/__main__.py +0 -0
  11. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/actions.py +0 -0
  12. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/agents/__init__.py +0 -0
  13. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/agents/agent.py +0 -0
  14. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/agents/callbacks.py +0 -0
  15. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/agents/config.py +0 -0
  16. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/agents/personas.py +0 -0
  17. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/agents/tools.py +0 -0
  18. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/analyze.py +0 -0
  19. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/app.py +0 -0
  20. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/bindings.py +0 -0
  21. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/chart.py +0 -0
  22. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/chat.py +0 -0
  23. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/cli_setup.py +0 -0
  24. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/deep_research.py +0 -0
  25. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/predefined_watchlists.py +0 -0
  26. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/session.py +0 -0
  27. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/settings_tab.py +0 -0
  28. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/theme.py +0 -0
  29. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/ticker_data.py +0 -0
  30. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/user_settings.py +0 -0
  31. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/user_watchlist.py +0 -0
  32. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/utils.py +0 -0
  33. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/verbose.py +0 -0
  34. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/widgets/__init__.py +0 -0
  35. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/widgets/chat_input.py +0 -0
  36. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/widgets/safe_rich_log.py +0 -0
  37. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/widgets/ticker_select.py +0 -0
  38. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/src/aipriceaction_terminal/workflows.py +0 -0
  39. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/conftest.py +0 -0
  40. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/openrouter_responses.py +0 -0
  41. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/test_app.py +0 -0
  42. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/test_chat.py +0 -0
  43. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/test_integration.py +0 -0
  44. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/test_settings_api.py +0 -0
  45. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/test_thinking.py +0 -0
  46. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/test_tool_call_streaming.py +0 -0
  47. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/test_tools.py +0 -0
  48. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/test_utils.py +0 -0
  49. {aipa_cli-0.1.43 → aipa_cli-0.1.45}/tests/test_workflows.py +0 -0
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.45] - 2026-05-31
9
+
10
+ ### Added
11
+ - `--period` flag to `fundamentals ratios` for quarter-specific queries (e.g. `"2024 Q2"`)
12
+
13
+ ### Changed
14
+ - Updated all skill docs: added "report"/"báo cáo" fundamentals hint, added `--period` docs for `ratios`
15
+
16
+ ## [0.1.44] - 2026-05-31
17
+
18
+ ### Added
19
+ - `--year` flag to `fundamentals rank` and `fundamentals screen` for historical year filtering
20
+ - `--period` flag to `fundamentals rank` and `fundamentals screen` for quarter-specific queries (e.g. `"2024 Q2"`)
21
+ - Reporting period (e.g. `period=2025`, `period=2026 Q1`) in headers of `fundamentals ratios`, `rank`, and `screen`
22
+
23
+ ### Changed
24
+ - Updated docs (AGENTS.md, skill files) with new flags and version gate >=0.1.44
25
+ - Bump SDK dependency to >=0.1.23
26
+
8
27
  ## [0.1.43] - 2026-05-31
9
28
 
10
29
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aipa-cli
3
- Version: 0.1.43
3
+ Version: 0.1.45
4
4
  Summary: Terminal TUI for AI-powered ticker analysis
5
5
  Project-URL: Homepage, https://github.com/quanhua92/aipriceaction
6
6
  Project-URL: Repository, https://github.com/quanhua92/aipriceaction
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Topic :: Office/Business :: Financial
17
17
  Requires-Python: >=3.13
18
- Requires-Dist: aipriceaction>=0.1.21
18
+ Requires-Dist: aipriceaction>=0.1.23
19
19
  Requires-Dist: langchain-core
20
20
  Requires-Dist: langchain-openai
21
21
  Requires-Dist: langgraph
@@ -9,7 +9,7 @@ license = "MIT"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
11
11
  dependencies = [
12
- "aipriceaction>=0.1.21",
12
+ "aipriceaction>=0.1.23",
13
13
  "textual>=3.0.0",
14
14
  "textual-autocomplete>=4.0.6",
15
15
  "textual-plotext>=0.4.1",
@@ -1,4 +1,4 @@
1
1
  """AIPriceAction Terminal - TUI chat interface for ticker analysis."""
2
2
 
3
- __version__ = "0.1.43"
3
+ __version__ = "0.1.45"
4
4
 
@@ -148,6 +148,7 @@ def run():
148
148
  p_fund_ratios.add_argument("--latest", action="store_true", help="Show only latest period (quarterly or yearly)")
149
149
  p_fund_ratios.add_argument("--yearly", action="store_true", help="Show only yearly reports")
150
150
  p_fund_ratios.add_argument("--year", type=int, default=None, help="Show specific year")
151
+ p_fund_ratios.add_argument("--period", type=str, default=None, help="Specific period like '2024' or '2024 Q2'")
151
152
  p_fund_ratios.add_argument("--no-yearly", action="store_true", help="Include quarterly reports (default, same as omitting the flag)")
152
153
  p_fund_ratios.add_argument("--category", default=None,
153
154
  choices=["valuation", "profitability", "leverage", "liquidity", "bank", "efficiency"],
@@ -157,6 +158,8 @@ def run():
157
158
  p_fund_rank = fund_sub.add_parser("rank", help="Rank tickers by a fundamental field")
158
159
  p_fund_rank.add_argument("--latest", action="store_true", help="Use latest period (quarterly or yearly) instead of yearly only")
159
160
  p_fund_rank.add_argument("--yearly", action="store_true", help="Use yearly reports only (default)")
161
+ p_fund_rank.add_argument("--year", type=int, default=None, help="Specific year (e.g. 2024)")
162
+ p_fund_rank.add_argument("--period", type=str, default=None, help="Specific period like '2024' or '2024 Q2'")
160
163
  p_fund_rank.add_argument("--sort-by", default="roe", choices=_fund_sort_choices)
161
164
  p_fund_rank.add_argument("--direction", default="desc", choices=["desc", "asc"])
162
165
  p_fund_rank.add_argument("--limit", type=int, default=10)
@@ -167,6 +170,8 @@ def run():
167
170
  p_fund_screen = fund_sub.add_parser("screen", help="Screen tickers by fundamental criteria")
168
171
  p_fund_screen.add_argument("--latest", action="store_true", help="Use latest period (quarterly or yearly) instead of yearly only")
169
172
  p_fund_screen.add_argument("--yearly", action="store_true", help="Use yearly reports only (default)")
173
+ p_fund_screen.add_argument("--year", type=int, default=None, help="Specific year (e.g. 2024)")
174
+ p_fund_screen.add_argument("--period", type=str, default=None, help="Specific period like '2024' or '2024 Q2'")
170
175
  p_fund_screen.add_argument("tickers", nargs="*", default=[], help="Ticker symbols (default: all VN)")
171
176
  p_fund_screen.add_argument("--watchlist", default=None, help="Use watchlist as ticker source")
172
177
  p_fund_screen.add_argument("--source", default=None)
@@ -793,7 +793,14 @@ def _fund_ratios(args) -> None:
793
793
  return
794
794
 
795
795
  entries = fr.ratios
796
- if args.year is not None:
796
+ if args.period is not None:
797
+ from aipriceaction.fundamental_ranking import _parse_period
798
+ p_year, p_q = _parse_period(args.period)
799
+ if p_q is not None:
800
+ entries = [r for r in entries if r.year_report == p_year and r.length_report == p_q]
801
+ else:
802
+ entries = [r for r in entries if r.year_report == p_year]
803
+ elif args.year is not None:
797
804
  entries = [r for r in entries if r.year_report == args.year]
798
805
  elif args.latest:
799
806
  entries = entries[:1]
@@ -811,8 +818,7 @@ def _fund_ratios(args) -> None:
811
818
  categories = [args.category] if args.category else list(_CATEGORY_FIELDS.keys())
812
819
 
813
820
  for entry in entries:
814
- period_type = "yearly" if entry.length_report in (5, 12) else f"Q{entry.length_report}"
815
- print(f"\n=== {ticker} Financial Ratios (year={entry.year_report}, {period_type}) ===")
821
+ print(f"\n=== {ticker} Financial Ratios (period={_period_str(entry)}) ===")
816
822
 
817
823
  for cat_name in categories:
818
824
  fields = _CATEGORY_FIELDS[cat_name]
@@ -827,6 +833,16 @@ def _fund_ratios(args) -> None:
827
833
  print(f"\n Total entries: {fr.count} | Showing: {len(entries)}")
828
834
 
829
835
 
836
+ def _period_str(entry) -> str:
837
+ if entry is None:
838
+ return "N/A"
839
+ length = entry.length_report
840
+ year = entry.year_report
841
+ if length in (5, 12) or entry.ratio_type == "RATIO_YEAR":
842
+ return str(year)
843
+ return f"{year} Q{length}"
844
+
845
+
830
846
  def _fund_rank(args) -> None:
831
847
  import pandas as pd
832
848
 
@@ -847,6 +863,8 @@ def _fund_rank(args) -> None:
847
863
  limit=args.limit,
848
864
  source=_resolve_source(args.source),
849
865
  yearly_only=not args.latest,
866
+ year=args.year,
867
+ period=args.period,
850
868
  )
851
869
 
852
870
  if not entries:
@@ -874,8 +892,14 @@ def _fund_rank(args) -> None:
874
892
  "industry": (e.industry or "")[:25],
875
893
  })
876
894
 
895
+ if args.period:
896
+ period = args.period
897
+ elif entries:
898
+ period = _period_str(entries[0].latest_ratio)
899
+ else:
900
+ period = "latest" if args.latest else "yearly"
877
901
  df = pd.DataFrame(rows)
878
- print(f"\n=== Top {len(entries)} by {sort_field} ({args.direction}) ===")
902
+ print(f"\n=== Top {len(entries)} by {sort_field} ({args.direction}, period={period}) ===")
879
903
  print(df.to_string(index=False))
880
904
 
881
905
 
@@ -899,6 +923,8 @@ def _fund_screen(args) -> None:
899
923
  limit=args.limit,
900
924
  source=_resolve_source(args.source),
901
925
  yearly_only=not args.latest,
926
+ year=args.year,
927
+ period=args.period,
902
928
  pe_min=args.pe_min, pe_max=args.pe_max,
903
929
  pb_min=args.pb_min, pb_max=args.pb_max,
904
930
  roe_min=args.roe_min, roe_max=args.roe_max,
@@ -949,8 +975,14 @@ def _fund_screen(args) -> None:
949
975
  filters.append(f"{attr_name}={v}")
950
976
  filter_desc = ", ".join(filters) if filters else "no filters"
951
977
 
978
+ if args.period:
979
+ period = args.period
980
+ elif entries:
981
+ period = _period_str(entries[0].latest_ratio)
982
+ else:
983
+ period = "latest" if args.latest else "yearly"
952
984
  df = pd.DataFrame(rows)
953
- print(f"\n=== Screened: {filter_desc} ({len(entries)} match, by {sort_field} {args.direction}) ===")
985
+ print(f"\n=== Screened: {filter_desc} ({len(entries)} match, by {sort_field} {args.direction}, period={period}) ===")
954
986
  print(df.to_string(index=False))
955
987
 
956
988
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes