TradeChart 2.1.0__tar.gz → 2.1.2__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 (37) hide show
  1. {tradechart-2.1.0 → tradechart-2.1.2}/PKG-INFO +2 -2
  2. {tradechart-2.1.0 → tradechart-2.1.2}/README.md +1 -1
  3. {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/PKG-INFO +2 -2
  4. {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/SOURCES.txt +1 -0
  5. {tradechart-2.1.0 → tradechart-2.1.2}/pyproject.toml +1 -1
  6. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/__init__.py +1 -1
  7. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/heatmap.py +12 -12
  8. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/renderer.py +2 -0
  9. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/core/engine.py +5 -1
  10. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/providers/tradingview_provider.py +3 -4
  11. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/providers/yfinance_provider.py +3 -4
  12. tradechart-2.1.2/tradechart/utils/install.py +66 -0
  13. {tradechart-2.1.0 → tradechart-2.1.2}/LICENSE +0 -0
  14. {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/dependency_links.txt +0 -0
  15. {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/requires.txt +0 -0
  16. {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/top_level.txt +0 -0
  17. {tradechart-2.1.0 → tradechart-2.1.2}/setup.cfg +0 -0
  18. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/__init__.py +0 -0
  19. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/indicators.py +0 -0
  20. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/themes.py +0 -0
  21. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/watermark.py +0 -0
  22. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/config/__init__.py +0 -0
  23. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/config/logger.py +0 -0
  24. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/config/settings.py +0 -0
  25. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/core/__init__.py +0 -0
  26. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/__init__.py +0 -0
  27. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/fetcher.py +0 -0
  28. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/groups.py +0 -0
  29. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/models.py +0 -0
  30. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/provider_base.py +0 -0
  31. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/providers/__init__.py +0 -0
  32. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/providers/stooq_provider.py +0 -0
  33. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/utils/__init__.py +0 -0
  34. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/utils/exceptions.py +0 -0
  35. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/utils/formatting.py +0 -0
  36. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/utils/validation.py +0 -0
  37. {tradechart-2.1.0 → tradechart-2.1.2}/tradechart_examples/discord_demo_bot/discord_bot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TradeChart
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Production-quality financial chart generator — candlestick, line, area, OHLC, Heikin-Ashi, performance heatmaps, and sector group charts.
5
5
  Author-email: "TRADELY.DEV" <dev@tradely.dev>
6
6
  License: Apache-2.0
@@ -45,7 +45,7 @@ Requires-Dist: build>=1.0; extra == "dev"
45
45
  Requires-Dist: twine>=5.0; extra == "dev"
46
46
  Dynamic: license-file
47
47
 
48
- # TradeChart — Library Edition v2.1.0
48
+ # TradeChart — Library Edition v2.1.2
49
49
 
50
50
  **Python → Financial Charts**
51
51
  Generate production-quality candlestick, line, area, OHLC, Heikin-Ashi, and performance heatmap charts from code.
@@ -1,4 +1,4 @@
1
- # TradeChart — Library Edition v2.1.0
1
+ # TradeChart — Library Edition v2.1.2
2
2
 
3
3
  **Python → Financial Charts**
4
4
  Generate production-quality candlestick, line, area, OHLC, Heikin-Ashi, and performance heatmap charts from code.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TradeChart
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Production-quality financial chart generator — candlestick, line, area, OHLC, Heikin-Ashi, performance heatmaps, and sector group charts.
5
5
  Author-email: "TRADELY.DEV" <dev@tradely.dev>
6
6
  License: Apache-2.0
@@ -45,7 +45,7 @@ Requires-Dist: build>=1.0; extra == "dev"
45
45
  Requires-Dist: twine>=5.0; extra == "dev"
46
46
  Dynamic: license-file
47
47
 
48
- # TradeChart — Library Edition v2.1.0
48
+ # TradeChart — Library Edition v2.1.2
49
49
 
50
50
  **Python → Financial Charts**
51
51
  Generate production-quality candlestick, line, area, OHLC, Heikin-Ashi, and performance heatmap charts from code.
@@ -30,5 +30,6 @@ tradechart/providers/yfinance_provider.py
30
30
  tradechart/utils/__init__.py
31
31
  tradechart/utils/exceptions.py
32
32
  tradechart/utils/formatting.py
33
+ tradechart/utils/install.py
33
34
  tradechart/utils/validation.py
34
35
  tradechart_examples/discord_demo_bot/discord_bot.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "TradeChart"
7
- version = "2.1.0"
7
+ version = "2.1.2"
8
8
  description = "Production-quality financial chart generator — candlestick, line, area, OHLC, Heikin-Ashi, performance heatmaps, and sector group charts."
9
9
  readme = "README.md"
10
10
  license = {text = "Apache-2.0"}
@@ -72,7 +72,7 @@ from tradechart.utils.exceptions import (
72
72
  ConfigError,
73
73
  )
74
74
 
75
- __version__ = "2.1.0"
75
+ __version__ = "2.1.2"
76
76
  __all__ = [
77
77
  "terminal", "theme", "watermark", "config",
78
78
  "chart", "compare", "heatmap", "data", "export", "clear_cache",
@@ -25,6 +25,12 @@ from tradechart.charts.themes import get_theme
25
25
  from tradechart.charts.watermark import stamp_logo
26
26
  from tradechart.utils.exceptions import RenderError
27
27
 
28
+ # Fixed gap between tiles in data-coordinate units.
29
+ # The canvas spans [0, fig_aspect] × [0, 1], and since fig_aspect = fw/fh the
30
+ # x and y data units are identical in physical size (both = fh inches/unit).
31
+ # A single constant therefore produces visually uniform gaps on all four sides.
32
+ _TILE_GAP = 0.006
33
+
28
34
 
29
35
  # ── Colour map: deep-red → slate-neutral → deep-green ───────────────────────
30
36
 
@@ -248,20 +254,14 @@ class HeatmapRenderer:
248
254
  price = prices.get(ticker)
249
255
  color = _tile_color(pct, vrange)
250
256
 
251
- # Gap between tiles: 1.5 % of cell dimensions
252
- gap_x = (tx1 - tx0) * 0.015
253
- gap_y = (ty1 - ty0) * 0.015
254
-
255
- rect = mpatches.FancyBboxPatch(
256
- (tx0 + gap_x, ty0 + gap_y),
257
- (tx1 - tx0) - 2 * gap_x,
258
- (ty1 - ty0) - 2 * gap_y,
259
- boxstyle="round,pad=0.005",
257
+ rect = mpatches.Rectangle(
258
+ (tx0 + _TILE_GAP, ty0 + _TILE_GAP),
259
+ (tx1 - tx0) - 2 * _TILE_GAP,
260
+ (ty1 - ty0) - 2 * _TILE_GAP,
260
261
  facecolor=color,
261
- edgecolor=theme.bg_color,
262
- linewidth=1.5,
262
+ edgecolor="none",
263
+ linewidth=0,
263
264
  zorder=2,
264
- transform=ax.transData,
265
265
  )
266
266
  ax.add_patch(rect)
267
267
 
@@ -196,6 +196,8 @@ class ChartRenderer:
196
196
  self, df: pd.DataFrame, meta: MarketData, out: Path,
197
197
  fmt: str, theme: Theme, indicators: list[str], show_volume: bool,
198
198
  ) -> Path:
199
+ from tradechart.utils.install import ensure_package
200
+ ensure_package("mplfinance")
199
201
  import mplfinance as mpf
200
202
 
201
203
  mc = mpf.make_marketcolors(
@@ -310,6 +310,8 @@ class Engine:
310
310
  elif fmt == "json":
311
311
  df.to_json(out_path, orient="index", date_format="iso", indent=2)
312
312
  elif fmt == "xlsx":
313
+ from tradechart.utils.install import ensure_package
314
+ ensure_package("openpyxl")
313
315
  df.to_excel(out_path, engine="openpyxl")
314
316
 
315
317
  self._log.summary(f"✓ Data exported → {out_path}")
@@ -397,6 +399,8 @@ class Engine:
397
399
  the data is unavailable (indices, futures, etc.) map to ``0``, which
398
400
  tells the heatmap renderer to fall back to the minimum-cap floor.
399
401
  """
402
+ from tradechart.utils.install import ensure_package
403
+ ensure_package("yfinance")
400
404
  import yfinance as yf
401
405
 
402
406
  caps: dict[str, float] = {}
@@ -406,7 +410,7 @@ class Engine:
406
410
  cap = info.get("marketCap") or info.get("market_cap") or 0
407
411
  caps[ticker] = float(cap) if cap else 0.0
408
412
  if caps[ticker] > 0:
409
- self._log.detail("Market cap %s → $%,.0f", ticker, caps[ticker])
413
+ self._log.detail("Market cap %s → $%s", ticker, f"{caps[ticker]:,.0f}")
410
414
  else:
411
415
  self._log.detail("Market cap %s → unavailable", ticker)
412
416
  except Exception as exc:
@@ -30,10 +30,9 @@ class TradingViewProvider(BaseProvider):
30
30
 
31
31
  def fetch(self, ticker: str, duration: str) -> MarketData:
32
32
  log = get_logger()
33
- try:
34
- from tvDatafeed import TvDatafeed, Interval
35
- except ImportError as exc:
36
- raise RuntimeError("tvDatafeed is not installed. Run: pip install tvDatafeed") from exc
33
+ from tradechart.utils.install import ensure_package
34
+ ensure_package("tvDatafeed")
35
+ from tvDatafeed import TvDatafeed, Interval
37
36
 
38
37
  n_bars, interval_name = _DURATION_MAP.get(duration, (22, "in_daily"))
39
38
  interval = getattr(Interval, interval_name, Interval.in_daily)
@@ -15,10 +15,9 @@ class YFinanceProvider(BaseProvider):
15
15
 
16
16
  def fetch(self, ticker: str, duration: str) -> MarketData:
17
17
  log = get_logger()
18
- try:
19
- import yfinance as yf
20
- except ImportError as exc:
21
- raise RuntimeError("yfinance is not installed. Run: pip install yfinance") from exc
18
+ from tradechart.utils.install import ensure_package
19
+ ensure_package("yfinance")
20
+ import yfinance as yf
22
21
 
23
22
  period, interval = DURATION_RESOLUTION_MAP.get(duration, ("1mo", "1d"))
24
23
  log.detail("yfinance: downloading %s period=%s interval=%s", ticker, period, interval)
@@ -0,0 +1,66 @@
1
+ """Runtime auto-installer for optional and required package dependencies.
2
+
3
+ When a package is missing, ``ensure_package`` installs it via pip in the same
4
+ Python environment and then verifies the import succeeds before returning.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import importlib
10
+ import subprocess
11
+ import sys
12
+
13
+
14
+ def ensure_package(pip_name: str, import_name: str | None = None) -> None:
15
+ """Ensure *pip_name* is importable, installing it silently if not.
16
+
17
+ Parameters
18
+ ----------
19
+ pip_name : str
20
+ The name used with ``pip install`` (e.g. ``"mplfinance"``).
21
+ import_name : str | None
22
+ The module name used with ``import`` when it differs from *pip_name*
23
+ (e.g. ``"cv2"`` for ``pip_name="opencv-python"``). Defaults to
24
+ *pip_name*.
25
+
26
+ Raises
27
+ ------
28
+ RuntimeError
29
+ If pip installation fails or the package still cannot be imported
30
+ after installation (e.g. wrong Python environment).
31
+ """
32
+ module_name = import_name or pip_name
33
+
34
+ try:
35
+ importlib.import_module(module_name)
36
+ return # already available
37
+ except ImportError:
38
+ pass
39
+
40
+ # Lazy import to avoid circular dependency at module load time
41
+ from tradechart.config.logger import get_logger
42
+ get_logger().detail("Auto-installing '%s' …", pip_name)
43
+
44
+ result = subprocess.run(
45
+ [sys.executable, "-m", "pip", "install", pip_name, "--quiet"],
46
+ capture_output=True,
47
+ text=True,
48
+ )
49
+
50
+ if result.returncode != 0:
51
+ raise RuntimeError(
52
+ f"Auto-install of '{pip_name}' failed.\n"
53
+ f"Run manually: pip install {pip_name}\n"
54
+ f"pip stderr: {result.stderr.strip()}"
55
+ )
56
+
57
+ # Invalidate importlib caches so the newly installed package is visible
58
+ importlib.invalidate_caches()
59
+
60
+ try:
61
+ importlib.import_module(module_name)
62
+ except ImportError:
63
+ raise RuntimeError(
64
+ f"'{pip_name}' was installed but '{module_name}' still cannot be "
65
+ f"imported. Try restarting your Python session."
66
+ )
File without changes
File without changes