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.
- {tradechart-2.1.0 → tradechart-2.1.2}/PKG-INFO +2 -2
- {tradechart-2.1.0 → tradechart-2.1.2}/README.md +1 -1
- {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/PKG-INFO +2 -2
- {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/SOURCES.txt +1 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/pyproject.toml +1 -1
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/__init__.py +1 -1
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/heatmap.py +12 -12
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/renderer.py +2 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/core/engine.py +5 -1
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/providers/tradingview_provider.py +3 -4
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/providers/yfinance_provider.py +3 -4
- tradechart-2.1.2/tradechart/utils/install.py +66 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/LICENSE +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/dependency_links.txt +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/requires.txt +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/TradeChart.egg-info/top_level.txt +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/setup.cfg +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/__init__.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/indicators.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/themes.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/charts/watermark.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/config/__init__.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/config/logger.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/config/settings.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/core/__init__.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/__init__.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/fetcher.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/groups.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/models.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/data/provider_base.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/providers/__init__.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/providers/stooq_provider.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/utils/__init__.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/utils/exceptions.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/utils/formatting.py +0 -0
- {tradechart-2.1.0 → tradechart-2.1.2}/tradechart/utils/validation.py +0 -0
- {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.
|
|
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.
|
|
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,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TradeChart
|
|
3
|
-
Version: 2.1.
|
|
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.
|
|
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.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "TradeChart"
|
|
7
|
-
version = "2.1.
|
|
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"}
|
|
@@ -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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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=
|
|
262
|
-
linewidth=
|
|
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 →
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|