Premchan369 commited on
Commit
7370e85
·
verified ·
1 Parent(s): de8787e

Upload advanced_features_part1.py

Browse files
Files changed (1) hide show
  1. advanced_features_part1.py +99 -0
advanced_features_part1.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Advanced Feature Engineering Part 1 - Microstructure & Cross-Sectional"""
2
+ import numpy as np
3
+ import pandas as pd
4
+ from typing import Dict, List, Optional
5
+ import warnings
6
+ warnings.filterwarnings('ignore')
7
+
8
+
9
+ class MicrostructureFeatures:
10
+ """Market microstructure features from OHLCV data"""
11
+
12
+ @staticmethod
13
+ def amihud_illiquidity(close, volume, window=21):
14
+ """Amihud (2002) illiquidity: avg |return| / dollar_volume"""
15
+ dollar_vol = close * volume
16
+ abs_ret = close.pct_change().abs()
17
+ return (abs_ret / dollar_vol).rolling(window).mean()
18
+
19
+ @staticmethod
20
+ def kyle_lambda(close, volume, window=21):
21
+ """Kyle's lambda: price impact per unit volume"""
22
+ abs_ret = close.pct_change().abs()
23
+ signed_vol = volume * np.sign(close.pct_change())
24
+ cov = abs_ret.rolling(window).cov(signed_vol)
25
+ var = signed_vol.rolling(window).var()
26
+ return cov / var.replace(0, np.nan)
27
+
28
+ @staticmethod
29
+ def bid_ask_spread_proxy(high, low, close, window=21):
30
+ """Corwin & Schultz (2012) spread estimator"""
31
+ beta = ((high - low) ** 2).rolling(window).sum()
32
+ spread = 2 * (np.exp(np.sqrt(2*beta) - beta) - 1) / (1 + np.exp(np.sqrt(2*beta) - beta))
33
+ return spread
34
+
35
+ @staticmethod
36
+ def vwap(close, high, low, volume, window=14):
37
+ """Volume-weighted average price"""
38
+ typical_price = (high + low + close) / 3
39
+ vp = typical_price * volume
40
+ cum_vp = vp.rolling(window).sum()
41
+ cum_vol = volume.rolling(window).sum()
42
+ return cum_vp / cum_vol.replace(0, np.nan)
43
+
44
+ @staticmethod
45
+ def roll_spread(close, window=20):
46
+ """Roll (1984) effective spread estimator"""
47
+ delta_p = close.diff()
48
+ cov = delta_p.rolling(window).apply(lambda x: np.cov(x[:-1], x[1:])[0,1])
49
+ return 2 * np.sqrt(-cov.clip(upper=0))
50
+
51
+ @staticmethod
52
+ def compute_all(close, high, low, volume):
53
+ """Compute all microstructure features"""
54
+ features = pd.DataFrame(index=close.index)
55
+ features['amihud_illiquidity'] = MicrostructureFeatures.amihud_illiquidity(close, volume)
56
+ features['kyle_lambda'] = MicrostructureFeatures.kyle_lambda(close, volume)
57
+ features['bid_ask_spread'] = MicrostructureFeatures.bid_ask_spread_proxy(high, low, close)
58
+ features['vwap_ratio'] = close / MicrostructureFeatures.vwap(close, high, low, volume)
59
+ features['roll_spread'] = MicrostructureFeatures.roll_spread(close)
60
+ # Dollar volume features
61
+ dollar_vol = close * volume
62
+ features['dollar_vol_rank'] = dollar_vol.rolling(63).rank(pct=True)
63
+ features['volume_trend'] = volume.rolling(21).mean() / volume.rolling(63).mean() - 1
64
+ features['volume_delta'] = ((close > close.shift(1)).astype(float) * volume - \
65
+ (close < close.shift(1)).astype(float) * volume) / volume.rolling(21).mean()
66
+ return features
67
+
68
+
69
+ class CrossSectionalFeatures:
70
+ """Cross-sectional ranking and momentum features"""
71
+
72
+ @staticmethod
73
+ def momentum_score(returns, periods=[5, 21, 63, 126, 252]):
74
+ """Cross-sectional momentum ranking"""
75
+ features = pd.DataFrame(index=returns.index)
76
+ for p in periods:
77
+ cum_ret = returns.rolling(p).sum()
78
+ features[f'cs_mom_{p}d'] = cum_ret.rank(axis=1, pct=True)
79
+ return features
80
+
81
+ @staticmethod
82
+ def mean_reversion(returns, short=5, long=63):
83
+ """Short-term reversal vs medium-term momentum"""
84
+ short_ret = returns.rolling(short).sum()
85
+ long_ret = returns.rolling(long).sum()
86
+ features = pd.DataFrame(index=returns.index)
87
+ features['mr_signal'] = short_ret.rank(axis=1, pct=True) - long_ret.rank(axis=1, pct=True)
88
+ features['mr_short'] = -short_ret.rank(axis=1, pct=True)
89
+ return features
90
+
91
+ @staticmethod
92
+ def dispersion(returns, window=21):
93
+ """Cross-sectional return dispersion"""
94
+ features = pd.DataFrame(index=returns.index)
95
+ features['cs_std'] = returns.rolling(window).std(axis=1)
96
+ features['cs_range'] = returns.rolling(window).max(axis=1) - returns.rolling(window).min(axis=1)
97
+ features['cs_skew'] = returns.rolling(window).skew(axis=1)
98
+ features['cs_kurt'] = returns.rolling(window).kurt(axis=1)
99
+ return features