ellements 0.2.0__py3-none-any.whl
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.
- ellements/__init__.py +57 -0
- ellements/agents/__init__.py +45 -0
- ellements/agents/backend.py +100 -0
- ellements/agents/builder.py +303 -0
- ellements/agents/claude_backend.py +200 -0
- ellements/agents/controller.py +237 -0
- ellements/agents/openai_backend.py +187 -0
- ellements/agents/py.typed +0 -0
- ellements/agents/runner.py +358 -0
- ellements/agents/tools.py +30 -0
- ellements/benchmarking/__init__.py +52 -0
- ellements/benchmarking/harness.py +342 -0
- ellements/benchmarking/py.typed +0 -0
- ellements/benchmarking/results.py +173 -0
- ellements/cli/__init__.py +80 -0
- ellements/cli/adapters.py +73 -0
- ellements/cli/agent_tui.py +1178 -0
- ellements/cli/components.py +411 -0
- ellements/cli/printer.py +229 -0
- ellements/cli/py.typed +0 -0
- ellements/core/__init__.py +112 -0
- ellements/core/async_utils.py +42 -0
- ellements/core/budgeting/__init__.py +43 -0
- ellements/core/budgeting/client.py +276 -0
- ellements/core/budgeting/protocol.py +51 -0
- ellements/core/budgeting/trackers.py +177 -0
- ellements/core/caching/__init__.py +51 -0
- ellements/core/caching/cache.py +73 -0
- ellements/core/caching/client.py +300 -0
- ellements/core/caching/disk.py +128 -0
- ellements/core/caching/keys.py +97 -0
- ellements/core/caching/memory.py +104 -0
- ellements/core/chunking.py +262 -0
- ellements/core/config.py +180 -0
- ellements/core/exceptions.py +145 -0
- ellements/core/llm/__init__.py +46 -0
- ellements/core/llm/client.py +1124 -0
- ellements/core/llm/images.py +226 -0
- ellements/core/llm/messages.py +202 -0
- ellements/core/llm/model_params.py +66 -0
- ellements/core/llm/protocol.py +146 -0
- ellements/core/llm/requests.py +100 -0
- ellements/core/llm/structured.py +91 -0
- ellements/core/llm/wrapper.py +79 -0
- ellements/core/observability/__init__.py +39 -0
- ellements/core/observability/events.py +169 -0
- ellements/core/observability/jsonl_logger.py +244 -0
- ellements/core/observability/markdown_formatter.py +197 -0
- ellements/core/observability/observer.py +56 -0
- ellements/core/prompting/__init__.py +14 -0
- ellements/core/prompting/context.py +185 -0
- ellements/core/prompting/guideline.py +133 -0
- ellements/core/prompting/persona.py +267 -0
- ellements/core/prompting/sources.py +92 -0
- ellements/core/py.typed +0 -0
- ellements/core/rate_limit/__init__.py +44 -0
- ellements/core/rate_limit/bucket.py +85 -0
- ellements/core/rate_limit/client.py +216 -0
- ellements/core/rate_limit/protocol.py +27 -0
- ellements/core/templating.py +126 -0
- ellements/core/tools/__init__.py +51 -0
- ellements/core/tools/dialects.py +136 -0
- ellements/core/tools/executor.py +48 -0
- ellements/core/tools/protocol.py +36 -0
- ellements/core/tools/records.py +28 -0
- ellements/core/tools/registry.py +205 -0
- ellements/core/tools/schemas.py +80 -0
- ellements/core/tools/simple.py +119 -0
- ellements/core/tools/spec.py +33 -0
- ellements/domain_specific/__init__.py +7 -0
- ellements/domain_specific/finance/__init__.py +188 -0
- ellements/domain_specific/finance/calculations.py +837 -0
- ellements/domain_specific/finance/charts.py +426 -0
- ellements/domain_specific/finance/portfolio.py +129 -0
- ellements/domain_specific/finance/quant_analysis.py +279 -0
- ellements/domain_specific/finance/risk.py +362 -0
- ellements/domain_specific/finance/technical_indicators.py +241 -0
- ellements/domain_specific/finance/tools.py +483 -0
- ellements/domain_specific/finance/valuation.py +312 -0
- ellements/domain_specific/finance/yahoo_finance.py +1523 -0
- ellements/domain_specific/finance/yahoo_finance_models.py +321 -0
- ellements/domain_specific/py.typed +0 -0
- ellements/execution/__init__.py +56 -0
- ellements/execution/callbacks.py +149 -0
- ellements/execution/catalog.py +70 -0
- ellements/execution/collaborative.py +191 -0
- ellements/execution/config.py +135 -0
- ellements/execution/py.typed +0 -0
- ellements/execution/reflection.py +156 -0
- ellements/execution/self_consistency.py +189 -0
- ellements/execution/single_call.py +48 -0
- ellements/execution/strategies.py +237 -0
- ellements/execution/tree_of_thought.py +541 -0
- ellements/fslm/__init__.py +108 -0
- ellements/fslm/builtins.py +103 -0
- ellements/fslm/cli.py +199 -0
- ellements/fslm/context.py +50 -0
- ellements/fslm/definition.py +163 -0
- ellements/fslm/det.py +173 -0
- ellements/fslm/dsl.py +458 -0
- ellements/fslm/errors.py +38 -0
- ellements/fslm/evaluators.py +141 -0
- ellements/fslm/kernel.py +603 -0
- ellements/fslm/loading.py +123 -0
- ellements/fslm/models.py +623 -0
- ellements/fslm/nl.py +87 -0
- ellements/fslm/observers.py +107 -0
- ellements/fslm/persistence.py +72 -0
- ellements/fslm/py.typed +0 -0
- ellements/fslm/rendering.py +551 -0
- ellements/fslm/visualization.py +25 -0
- ellements/reporting/__init__.py +12 -0
- ellements/reporting/charts.py +364 -0
- ellements/reporting/html_generation.py +132 -0
- ellements/reporting/py.typed +0 -0
- ellements/reporting/visualization.py +73 -0
- ellements/standard_tools/__init__.py +22 -0
- ellements/standard_tools/py.typed +0 -0
- ellements/standard_tools/terminal.py +205 -0
- ellements/standard_tools/web/__init__.py +37 -0
- ellements/standard_tools/web/browser_viewer.py +214 -0
- ellements/standard_tools/web/crawler.py +454 -0
- ellements/standard_tools/web/search.py +399 -0
- ellements/standard_tools/web/youtube.py +1297 -0
- ellements-0.2.0.dist-info/METADATA +368 -0
- ellements-0.2.0.dist-info/RECORD +130 -0
- ellements-0.2.0.dist-info/WHEEL +5 -0
- ellements-0.2.0.dist-info/entry_points.txt +2 -0
- ellements-0.2.0.dist-info/licenses/LICENSE +42 -0
- ellements-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Technical analysis indicators with pandas-first APIs backed by TA-Lib."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def talib_available() -> bool:
|
|
14
|
+
"""Return True when TA-Lib can be imported."""
|
|
15
|
+
try:
|
|
16
|
+
import talib # noqa: F401
|
|
17
|
+
except ImportError:
|
|
18
|
+
return False
|
|
19
|
+
return True
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _load_talib() -> Any:
|
|
23
|
+
try:
|
|
24
|
+
import talib
|
|
25
|
+
except ImportError as exc:
|
|
26
|
+
raise RuntimeError(
|
|
27
|
+
"TA-Lib is required for technical indicators. "
|
|
28
|
+
"Install with: pip install TA-Lib"
|
|
29
|
+
) from exc
|
|
30
|
+
return talib
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TechnicalIndicatorSnapshot(BaseModel):
|
|
34
|
+
"""Latest values for the core technical indicator set."""
|
|
35
|
+
|
|
36
|
+
sample_size: int = 0
|
|
37
|
+
rsi: float | None = None
|
|
38
|
+
macd: float | None = None
|
|
39
|
+
macd_signal: float | None = None
|
|
40
|
+
macd_histogram: float | None = None
|
|
41
|
+
sma: float | None = None
|
|
42
|
+
ema: float | None = None
|
|
43
|
+
bollinger_upper: float | None = None
|
|
44
|
+
bollinger_middle: float | None = None
|
|
45
|
+
bollinger_lower: float | None = None
|
|
46
|
+
stochastic_k: float | None = None
|
|
47
|
+
stochastic_d: float | None = None
|
|
48
|
+
atr: float | None = None
|
|
49
|
+
adx: float | None = None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _as_float_series(series: pd.Series) -> pd.Series:
|
|
53
|
+
return pd.to_numeric(series, errors="coerce").astype("float64")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _validate_positive_int(name: str, value: int) -> None:
|
|
57
|
+
if value <= 0:
|
|
58
|
+
raise ValueError(f"{name} must be > 0.")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _normalize_price_input(
|
|
62
|
+
data: pd.Series | pd.DataFrame | Sequence[float],
|
|
63
|
+
) -> pd.DataFrame:
|
|
64
|
+
if isinstance(data, pd.Series):
|
|
65
|
+
frame = pd.DataFrame({"close": _as_float_series(data)})
|
|
66
|
+
frame.index = data.index
|
|
67
|
+
return frame.sort_index()
|
|
68
|
+
|
|
69
|
+
if isinstance(data, pd.DataFrame):
|
|
70
|
+
frame = data.copy()
|
|
71
|
+
if frame.empty:
|
|
72
|
+
raise ValueError("Input DataFrame is empty.")
|
|
73
|
+
|
|
74
|
+
normalized_columns: dict[object, str] = {}
|
|
75
|
+
for original in frame.columns:
|
|
76
|
+
raw = str(original).strip().lower().replace(" ", "_")
|
|
77
|
+
if raw in {"open", "high", "low", "close", "volume"}:
|
|
78
|
+
normalized_columns[original] = raw
|
|
79
|
+
elif raw in {"adj_close", "adjusted_close", "adjclose"}:
|
|
80
|
+
normalized_columns[original] = "adj_close"
|
|
81
|
+
|
|
82
|
+
if not normalized_columns:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"Input DataFrame must include close prices (close/adj_close)."
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
working = frame.rename(columns=normalized_columns)
|
|
88
|
+
selected_columns = [
|
|
89
|
+
col
|
|
90
|
+
for col in ["open", "high", "low", "close", "adj_close", "volume"]
|
|
91
|
+
if col in working.columns
|
|
92
|
+
]
|
|
93
|
+
working = working[selected_columns].copy()
|
|
94
|
+
|
|
95
|
+
if "close" not in working.columns:
|
|
96
|
+
if "adj_close" not in working.columns:
|
|
97
|
+
raise ValueError(
|
|
98
|
+
"Input DataFrame must include close prices (close/adj_close)."
|
|
99
|
+
)
|
|
100
|
+
working["close"] = working["adj_close"]
|
|
101
|
+
elif "adj_close" in working.columns:
|
|
102
|
+
working["close"] = working["close"].fillna(working["adj_close"])
|
|
103
|
+
|
|
104
|
+
for column in working.columns:
|
|
105
|
+
working[column] = _as_float_series(working[column])
|
|
106
|
+
|
|
107
|
+
return working.sort_index()
|
|
108
|
+
|
|
109
|
+
if not isinstance(data, Sequence):
|
|
110
|
+
raise ValueError("Unsupported input type for technical indicators.")
|
|
111
|
+
|
|
112
|
+
series = pd.Series(list(data), dtype="float64")
|
|
113
|
+
return pd.DataFrame({"close": _as_float_series(series)})
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _last_finite(series: pd.Series) -> float | None:
|
|
117
|
+
clean = series.replace([np.inf, -np.inf], np.nan).dropna()
|
|
118
|
+
if clean.empty:
|
|
119
|
+
return None
|
|
120
|
+
return float(clean.iloc[-1])
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def latest_indicator_snapshot(indicators: pd.DataFrame) -> TechnicalIndicatorSnapshot:
|
|
124
|
+
"""Extract latest finite values for each indicator column."""
|
|
125
|
+
return TechnicalIndicatorSnapshot(
|
|
126
|
+
sample_size=int(len(indicators)),
|
|
127
|
+
rsi=_last_finite(indicators["rsi"]),
|
|
128
|
+
macd=_last_finite(indicators["macd"]),
|
|
129
|
+
macd_signal=_last_finite(indicators["macd_signal"]),
|
|
130
|
+
macd_histogram=_last_finite(indicators["macd_histogram"]),
|
|
131
|
+
sma=_last_finite(indicators["sma"]),
|
|
132
|
+
ema=_last_finite(indicators["ema"]),
|
|
133
|
+
bollinger_upper=_last_finite(indicators["bollinger_upper"]),
|
|
134
|
+
bollinger_middle=_last_finite(indicators["bollinger_middle"]),
|
|
135
|
+
bollinger_lower=_last_finite(indicators["bollinger_lower"]),
|
|
136
|
+
stochastic_k=_last_finite(indicators["stochastic_k"]),
|
|
137
|
+
stochastic_d=_last_finite(indicators["stochastic_d"]),
|
|
138
|
+
atr=_last_finite(indicators["atr"]),
|
|
139
|
+
adx=_last_finite(indicators["adx"]),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def compute_technical_indicators(
|
|
144
|
+
data: pd.Series | pd.DataFrame | Sequence[float],
|
|
145
|
+
*,
|
|
146
|
+
rsi_period: int = 14,
|
|
147
|
+
macd_fast: int = 12,
|
|
148
|
+
macd_slow: int = 26,
|
|
149
|
+
macd_signal: int = 9,
|
|
150
|
+
sma_period: int = 20,
|
|
151
|
+
ema_period: int = 20,
|
|
152
|
+
bollinger_window: int = 20,
|
|
153
|
+
bollinger_dev: float = 2.0,
|
|
154
|
+
stochastic_window: int = 14,
|
|
155
|
+
stochastic_smooth_window: int = 3,
|
|
156
|
+
atr_window: int = 14,
|
|
157
|
+
adx_window: int = 14,
|
|
158
|
+
) -> tuple[pd.DataFrame, TechnicalIndicatorSnapshot]:
|
|
159
|
+
"""Compute the technical indicator set on Series/DataFrame inputs.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
tuple[pd.DataFrame, TechnicalIndicatorSnapshot]:
|
|
163
|
+
- DataFrame with one column per indicator and aligned index.
|
|
164
|
+
- Snapshot with latest finite values.
|
|
165
|
+
"""
|
|
166
|
+
_validate_positive_int("rsi_period", rsi_period)
|
|
167
|
+
_validate_positive_int("macd_fast", macd_fast)
|
|
168
|
+
_validate_positive_int("macd_slow", macd_slow)
|
|
169
|
+
_validate_positive_int("macd_signal", macd_signal)
|
|
170
|
+
_validate_positive_int("sma_period", sma_period)
|
|
171
|
+
_validate_positive_int("ema_period", ema_period)
|
|
172
|
+
_validate_positive_int("bollinger_window", bollinger_window)
|
|
173
|
+
_validate_positive_int("stochastic_window", stochastic_window)
|
|
174
|
+
_validate_positive_int("stochastic_smooth_window", stochastic_smooth_window)
|
|
175
|
+
_validate_positive_int("atr_window", atr_window)
|
|
176
|
+
_validate_positive_int("adx_window", adx_window)
|
|
177
|
+
if bollinger_dev <= 0:
|
|
178
|
+
raise ValueError("bollinger_dev must be > 0.")
|
|
179
|
+
|
|
180
|
+
talib = _load_talib()
|
|
181
|
+
frame = _normalize_price_input(data)
|
|
182
|
+
if frame.empty:
|
|
183
|
+
raise ValueError("No price data available to compute indicators.")
|
|
184
|
+
|
|
185
|
+
close = frame["close"].to_numpy(dtype="float64")
|
|
186
|
+
rsi = talib.RSI(close, timeperiod=rsi_period)
|
|
187
|
+
macd, macd_signal_line, macd_hist = talib.MACD(
|
|
188
|
+
close,
|
|
189
|
+
fastperiod=macd_fast,
|
|
190
|
+
slowperiod=macd_slow,
|
|
191
|
+
signalperiod=macd_signal,
|
|
192
|
+
)
|
|
193
|
+
sma = talib.SMA(close, timeperiod=sma_period)
|
|
194
|
+
ema = talib.EMA(close, timeperiod=ema_period)
|
|
195
|
+
bb_upper, bb_middle, bb_lower = talib.BBANDS(
|
|
196
|
+
close,
|
|
197
|
+
timeperiod=bollinger_window,
|
|
198
|
+
nbdevup=bollinger_dev,
|
|
199
|
+
nbdevdn=bollinger_dev,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
nan_line = np.full(len(frame), np.nan, dtype="float64")
|
|
203
|
+
stochastic_k = nan_line.copy()
|
|
204
|
+
stochastic_d = nan_line.copy()
|
|
205
|
+
atr = nan_line.copy()
|
|
206
|
+
adx = nan_line.copy()
|
|
207
|
+
|
|
208
|
+
if {"high", "low"}.issubset(frame.columns):
|
|
209
|
+
high = frame["high"].to_numpy(dtype="float64")
|
|
210
|
+
low = frame["low"].to_numpy(dtype="float64")
|
|
211
|
+
stochastic_k, stochastic_d = talib.STOCH(
|
|
212
|
+
high,
|
|
213
|
+
low,
|
|
214
|
+
close,
|
|
215
|
+
fastk_period=stochastic_window,
|
|
216
|
+
slowk_period=stochastic_smooth_window,
|
|
217
|
+
slowd_period=stochastic_smooth_window,
|
|
218
|
+
)
|
|
219
|
+
atr = talib.ATR(high, low, close, timeperiod=atr_window)
|
|
220
|
+
adx = talib.ADX(high, low, close, timeperiod=adx_window)
|
|
221
|
+
|
|
222
|
+
indicators = pd.DataFrame(
|
|
223
|
+
{
|
|
224
|
+
"rsi": rsi,
|
|
225
|
+
"macd": macd,
|
|
226
|
+
"macd_signal": macd_signal_line,
|
|
227
|
+
"macd_histogram": macd_hist,
|
|
228
|
+
"sma": sma,
|
|
229
|
+
"ema": ema,
|
|
230
|
+
"bollinger_upper": bb_upper,
|
|
231
|
+
"bollinger_middle": bb_middle,
|
|
232
|
+
"bollinger_lower": bb_lower,
|
|
233
|
+
"stochastic_k": stochastic_k,
|
|
234
|
+
"stochastic_d": stochastic_d,
|
|
235
|
+
"atr": atr,
|
|
236
|
+
"adx": adx,
|
|
237
|
+
},
|
|
238
|
+
index=frame.index,
|
|
239
|
+
dtype="float64",
|
|
240
|
+
)
|
|
241
|
+
return indicators, latest_indicator_snapshot(indicators)
|