# wael-lib

**Technical Analysis, Data Handling & Sequence Utilities for Trading Applications**

[![PyPI version](https://badge.fury.io/py/wael-lib.svg)](https://pypi.org/project/wael-lib/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

---

## Installation

```bash
pip install wael-lib
```

## Quick Start

```python
import pandas as pd
from wael_lib import TechAnalysis, WDataHandler, SequenceCreator, SequenceScaler

ta = TechAnalysis()
dh = WDataHandler()

df = pd.read_csv("OHLCV.csv")

# Indicators
df["SMA20"]  = ta.sma(df["Close"], 20)
df["RSI14"]  = ta.rsi(df["Close"], 14)
macd, signal, hist = ta.macd(df["Close"])
df["PriceRange"] = ta.categorize_price_ranges(df["Close"])

# Clean outliers
df["Volume"] = dh.handle_outliers(df, "Volume")

# Build LSTM sequences
seq = SequenceCreator(window=30, horizon=1)
X_seq, y_seq = seq.create_sequences(df[["Close", "Volume"]], df["Target"])
scaler = SequenceScaler()
X_scaled = scaler.fit_transform(X_seq)
```

---

## Classes

| Class | Purpose |
|---|---|
| `TechAnalysis` | 30+ indicators, trend/structure detection, candlestick patterns |
| `WDataHandler` | IQR outlier clipping, empirical CDF |
| `SequenceCreator` | Sliding-window sequences for LSTM / GRU |
| `SequenceScaler` | MinMaxScaler for 3-D tensors (sklearn-compatible) |
| `MySequenceScaler` | StandardScaler for 3-D tensors (sklearn-compatible) |

---

## API Reference

### `TechAnalysis`

#### Moving Averages

| Method | Signature | Description |
|---|---|---|
| `sma` | `sma(source, length) → pd.Series` | Simple Moving Average |
| `ema` | `ema(source, length) → pd.Series` | Exponential Moving Average |
| `wma` | `wma(source, length) → pd.Series` | Weighted Moving Average |
| `tema` | `tema(source, length) → pd.Series` | Triple Exponential Moving Average |

#### Oscillators & Momentum

| Method | Signature | Description |
|---|---|---|
| `rsi` | `rsi(source, length) → pd.Series` | Relative Strength Index (0–100) |
| `stoch` | `stoch(high, low, close, k_length, d_length) → (pd.Series, pd.Series)` | Stochastic Oscillator (%K, %D) |
| `mfi` | `mfi(high, low, close, volume, length) → pd.Series` | Money Flow Index (0–100) |
| `adx` | `adx(high, low, close, length) → pd.Series` | Average Directional Index |
| `macd` | `macd(source, fast=12, slow=26, signal=9) → (line, signal, hist)` | MACD (3 series) |
| `tema_macd` | `tema_macd(source, fast=12, slow=24, signal=9) → (line, signal, hist)` | TEMA-based MACD (reduced lag) |
| `PriceExtOsc` | `PriceExtOsc(high, low, length) → pd.Series` | Price Extension Oscillator (0–100) |
| `accumulation` | `accumulation(df, length=14) → pd.Series` | Accumulation/Distribution |
| `acc_osc` | `acc_osc(df, length=14) → pd.Series` | Accumulation Oscillator (0–100) |
| `extremes` | `extremes(source, length=14) → pd.Series` | Min-max normalisation (0–100) |
| `categorize_price_ranges` | `categorize_price_ranges(source) → pd.Series` | Categorize prices into descriptive ranges (Very Low, Low, Moderate, High, Very High) |

#### Volatility

| Method | Signature | Description |
|---|---|---|
| `atr` | `atr(high, low, close, length) → pd.Series` | Average True Range |
| `stdev` | `stdev(source, length) → pd.Series` | Rolling Standard Deviation |

#### Utility Functions

| Method | Signature | Description |
|---|---|---|
| `highest` | `highest(source, length) → pd.Series` | Rolling maximum |
| `lowest` | `lowest(source, length) → pd.Series` | Rolling minimum |
| `change` | `change(source, length) → pd.Series` | Period-over-period difference |
| `valuewhen` | `valuewhen(condition, value, occurrence) → pd.Series` | Value at last condition=True |
| `barssince` | `barssince(condition) → pd.Series` | Bars since last condition=True |
| `math_max` | `math_max(a, b) → pd.Series` | Element-wise maximum |
| `math_min` | `math_min(a, b) → pd.Series` | Element-wise minimum |
| `math_round` | `math_round(source) → pd.Series` | Round to nearest integer |
| `math_abs` | `math_abs(source) → pd.Series` | Absolute value |
| `math_sum` | `math_sum(source, length) → pd.Series` | Rolling sum |

#### Pivot Points

| Method | Signature | Description |
|---|---|---|
| `pivot_high` | `pivot_high(high, left, right, fill_pivots=True) → pd.Series` | Detect swing highs |
| `pivot_low` | `pivot_low(low, left, right, fill_pivots=True) → pd.Series` | Detect swing lows |

#### Trend & Structure Detection

| Method | Signature | Description |
|---|---|---|
| `rw_top` | `rw_top(data, curr_index, order) → bool` | Local top check |
| `rw_bottom` | `rw_bottom(data, curr_index, order) → bool` | Local bottom check |
| `rw_extremes` | `rw_extremes(data, order) → (tops, bottoms)` | All local extremes |
| `detect_trend` | `detect_trend(df, row_index, tops, bottoms) → str` | Uptrend / Downtrend / Sideways |
| `detect_bos_choch` | `detect_bos_choch(df, row_index, tops, bottoms) → str` | Break of Structure or Change of Character |
| `detect_formations` | `detect_formations(df, row_index, tops, bottoms) → str` | Chart formations (H&S, Double Top, Triangles, …) |
| `detect_candlestick_pattern` | `detect_candlestick_pattern(df, row_index) → str` | 12 candlestick patterns (Engulfing, Hammer, Doji, …) |

---

### `WDataHandler`

| Method | Signature | Description |
|---|---|---|
| `handle_outliers` | `handle_outliers(df, feature) → pd.Series` | IQR-based outlier clipping |
| `calculate_ECDF` | `calculate_ECDF(data, percent=False) → (sorted, ecdf)` | Empirical CDF (survival form) |

**Example — Outlier Clipping:**

```python
dh = WDataHandler()
df["Volume"] = dh.handle_outliers(df, "Volume")
```

**Example — ECDF:**

```python
sorted_vals, ecdf = dh.calculate_ECDF(df["Close"].values)
```

---

### `SequenceCreator`

Create sliding-window sequences for LSTM / GRU models.

| Method | Signature | Description |
|---|---|---|
| `__init__` | `SequenceCreator(window=10, horizon=1)` | Set window and horizon |
| `create_sequences` | `create_sequences(X, y) → (X_seq, y_seq)` | Input/target pairs `(batch, timesteps, features)` |
| `create_X_sequences` | `create_X_sequences(X) → X_seq` | Input-only sequences for inference |

**Example:**

```python
seq = SequenceCreator(window=30, horizon=1)
X_seq, y_seq = seq.create_sequences(df[["Close", "Volume"]], df["Target"])
# X_seq.shape → (n_samples - 30, 30, 2)

# For prediction on new data
X_pred = seq.create_X_sequences(new_data[["Close", "Volume"]])
predictions = model.predict(X_pred)
```

---

### `SequenceScaler`

Scikit-learn compatible `MinMaxScaler` wrapper for 3-D tensors.

| Method | Signature | Description |
|---|---|---|
| `fit` | `fit(X, y=None) → self` | Learn min/max from 3-D data |
| `transform` | `transform(X) → np.ndarray` | Scale to [0, 1] |
| `fit_transform` | `fit_transform(X, y=None) → np.ndarray` | Fit then transform |

**Example:**

```python
scaler = SequenceScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled  = scaler.transform(X_test)
```

---

### `MySequenceScaler`

Same API as `SequenceScaler` but uses `StandardScaler` (zero-mean, unit-variance).

**Example:**

```python
scaler = MySequenceScaler()
X_train_scaled = scaler.fit_transform(X_train)
```

---

## Dependencies

| Package | Version |
|---|---|
| pandas | >= 2.0.0 |
| numpy | >= 1.24.0 |
| scikit-learn | >= 1.3.0 |

## License

MIT — see [LICENSE](LICENSE) for details.

## Author

**Wael Fouda** — [GitHub](https://github.com/WaelFouda/wael-lib)
