0
# Pandas Extensions
1
2
FFN automatically extends pandas Series and DataFrame objects with financial analysis methods, enabling seamless method chaining and functional composition. All FFN functions become available as methods on pandas objects when FFN is imported.
3
4
## Capabilities
5
6
### Extension System
7
8
Core functionality that adds FFN methods to pandas objects automatically.
9
10
```python { .api }
11
def extend_pandas():
12
"""
13
Extend pandas Series and DataFrame with all FFN functions as methods.
14
15
Called automatically when FFN is imported. Enables method chaining like:
16
prices.to_returns().calc_sharpe()
17
18
Returns:
19
None (modifies pandas classes in-place)
20
"""
21
```
22
23
### Available Extension Methods
24
25
When FFN is imported, all the following methods become available on pandas Series and DataFrame objects:
26
27
#### Return and Price Calculations
28
```python { .api }
29
# Convert between prices and returns
30
Series.to_returns() # Convert prices to simple returns
31
Series.to_log_returns() # Convert prices to log returns
32
Series.to_price_index(start=100) # Convert returns to price index
33
Series.rebase(value=100) # Rebase series to specified value
34
DataFrame.rebase(value=100) # Rebase all columns to specified value
35
```
36
37
#### Performance Analysis Methods
38
```python { .api }
39
# Performance statistics and analysis
40
Series.calc_perf_stats(rf=0.0, annualization_factor=252) # Create PerformanceStats object
41
DataFrame.calc_stats() # Create GroupStats object for multiple series
42
Series.calc_cagr() # Calculate Compound Annual Growth Rate
43
Series.calc_total_return() # Calculate total return
44
```
45
46
#### Risk Analysis Methods
47
```python { .api }
48
# Risk and drawdown analysis
49
Series.calc_max_drawdown() # Calculate maximum drawdown
50
Series.to_drawdown_series() # Convert to drawdown series
51
DataFrame.to_drawdown_series() # Calculate drawdowns for all columns
52
Series.calc_sharpe(rf=0.0, nperiods=None, annualize=True) # Sharpe ratio
53
DataFrame.calc_sharpe(rf=0.0, nperiods=None, annualize=True) # Sharpe for all columns
54
Series.calc_sortino_ratio(rf=0.0, nperiods=None, annualize=True) # Sortino ratio
55
Series.calc_calmar_ratio() # Calmar ratio
56
Series.to_ulcer_index() # Ulcer Index calculation
57
Series.to_ulcer_performance_index(rf=0.0, nperiods=None) # Ulcer Performance Index
58
```
59
60
#### Portfolio Optimization Methods
61
```python { .api }
62
# Portfolio weight calculation methods
63
DataFrame.calc_inv_vol_weights() # Inverse volatility weights
64
DataFrame.calc_mean_var_weights(weight_bounds=(0.0, 1.0), rf=0.0, covar_method="ledoit-wolf", options=None) # Mean-variance weights
65
DataFrame.calc_erc_weights(initial_weights=None, risk_weights=None, covar_method="ledoit-wolf", risk_parity_method="ccd", maximum_iterations=100, tolerance=1e-8) # Equal risk contribution weights
66
```
67
68
#### Statistical Analysis Methods
69
```python { .api }
70
# Advanced statistical methods
71
DataFrame.calc_clusters(n=None, plot=False) # K-means clustering
72
DataFrame.calc_ftca(threshold=0.5) # Fast Threshold Clustering Algorithm
73
Series.calc_prob_mom(other_returns) # Probabilistic momentum
74
DataFrame.resample_returns(func, seed=0, num_trials=100) # Bootstrap resampling
75
```
76
77
#### Data Processing Methods
78
```python { .api }
79
# Data transformation and processing
80
Series.to_monthly(method="ffill", how="end") # Convert to monthly frequency
81
DataFrame.to_monthly(method="ffill", how="end") # Convert all columns to monthly
82
Series.asfreq_actual(freq, method="ffill", how="end", normalize=False) # Custom frequency conversion
83
DataFrame.drop_duplicate_cols() # Remove duplicate columns
84
Series.winsorize(axis=0, limits=0.01) # Winsorize outliers
85
DataFrame.winsorize(axis=0, limits=0.01) # Winsorize all columns
86
Series.rescale(min=0.0, max=1.0, axis=0) # Rescale to range
87
DataFrame.rescale(min=0.0, max=1.0, axis=0) # Rescale all columns
88
DataFrame.rollapply(window, fn) # Rolling function application
89
```
90
91
#### Excess Returns and Risk-Adjusted Methods
92
```python { .api }
93
# Risk-adjusted return calculations
94
Series.to_excess_returns(rf, nperiods=None) # Calculate excess returns
95
DataFrame.to_excess_returns(rf, nperiods=None) # Excess returns for all columns
96
Series.calc_information_ratio(benchmark_returns) # Information ratio vs benchmark
97
Series.calc_risk_return_ratio() # Return/risk ratio (Sharpe without rf)
98
```
99
100
#### Formatting and Display Methods
101
```python { .api }
102
# Data formatting methods
103
Series.as_percent(digits=2) # Format as percentage strings
104
DataFrame.as_percent(digits=2) # Format all values as percentages
105
Series.as_format(format_str=".2f") # Apply custom format string
106
DataFrame.as_format(format_str=".2f") # Format all values with custom string
107
```
108
109
#### Visualization Methods
110
```python { .api }
111
# Plotting and visualization
112
DataFrame.plot_heatmap(title="Heatmap", show_legend=True, show_labels=True, label_fmt=".2f", vmin=None, vmax=None, figsize=None, label_color="w", cmap="RdBu", **kwargs) # General heatmap
113
DataFrame.plot_corr_heatmap(**kwargs) # Correlation heatmap with sensible defaults
114
```
115
116
## Usage Examples
117
118
### Basic Method Chaining
119
120
```python
121
import ffn
122
import pandas as pd
123
124
# Download price data
125
prices = ffn.get('AAPL,MSFT,GOOGL', start='2020-01-01')
126
127
# Method chaining example - from prices to risk analysis
128
analysis = (prices
129
.rebase(100) # Normalize to 100
130
.to_returns() # Convert to returns
131
.dropna() # Remove NaN values
132
.calc_sharpe(rf=0.02) # Calculate Sharpe ratios
133
)
134
135
print("Sharpe ratios via method chaining:")
136
print(analysis)
137
```
138
139
### Performance Analysis Pipeline
140
141
```python
142
import ffn
143
144
# Create comprehensive performance pipeline
145
def analyze_asset(ticker, start_date, benchmark='SPY'):
146
"""Complete performance analysis using pandas extensions."""
147
148
# Get asset and benchmark data
149
asset_prices = ffn.get(ticker, start=start_date)
150
benchmark_prices = ffn.get(benchmark, start=start_date)
151
152
# Performance analysis using method chaining
153
asset_performance = (asset_prices
154
.iloc[:, 0] # Get first column as Series
155
.calc_perf_stats(rf=0.02) # Create PerformanceStats object
156
)
157
158
# Risk analysis
159
risk_metrics = {
160
'Max Drawdown': asset_prices.iloc[:, 0].calc_max_drawdown(),
161
'Sharpe Ratio': asset_prices.to_returns().dropna().iloc[:, 0].calc_sharpe(rf=0.02),
162
'Calmar Ratio': asset_prices.iloc[:, 0].calc_calmar_ratio()
163
}
164
165
# Comparative analysis
166
combined_returns = ffn.merge(
167
asset_prices.to_returns().dropna(),
168
benchmark_prices.to_returns().dropna()
169
)
170
171
correlations = combined_returns.corr()
172
173
return {
174
'performance': asset_performance,
175
'risk_metrics': risk_metrics,
176
'correlations': correlations,
177
'returns': combined_returns
178
}
179
180
# Run analysis
181
results = analyze_asset('AAPL', '2020-01-01')
182
print(f"AAPL CAGR: {results['performance'].cagr:.2%}")
183
print(f"AAPL Sharpe: {results['risk_metrics']['Sharpe Ratio']:.3f}")
184
```
185
186
### Portfolio Construction with Extensions
187
188
```python
189
import ffn
190
import numpy as np
191
192
# Multi-asset portfolio construction
193
assets = ['SPY', 'QQQ', 'IWM', 'EFA', 'EEM', 'BND']
194
prices = ffn.get(assets, start='2020-01-01')
195
returns = prices.to_returns().dropna()
196
197
# Different weighting schemes using pandas extensions
198
equal_weights = pd.Series(1/len(assets), index=returns.columns)
199
inv_vol_weights = returns.calc_inv_vol_weights()
200
erc_weights = returns.calc_erc_weights()
201
mean_var_weights = returns.calc_mean_var_weights(rf=0.02)
202
203
# Portfolio performance comparison
204
portfolios = {
205
'Equal Weight': equal_weights,
206
'Inverse Vol': inv_vol_weights,
207
'Risk Parity': erc_weights,
208
'Mean-Variance': mean_var_weights
209
}
210
211
# Calculate portfolio returns and statistics
212
portfolio_analysis = {}
213
for name, weights in portfolios.items():
214
# Portfolio returns using method chaining
215
portfolio_returns = (returns * weights).sum(axis=1)
216
portfolio_prices = portfolio_returns.to_price_index(start=100)
217
218
# Performance analysis via extensions
219
perf_stats = portfolio_prices.calc_perf_stats(rf=0.02)
220
221
portfolio_analysis[name] = {
222
'weights': weights,
223
'cagr': perf_stats.cagr,
224
'sharpe': perf_stats.daily_sharpe,
225
'max_dd': perf_stats.max_drawdown,
226
'calmar': perf_stats.calmar
227
}
228
229
# Display results
230
results_df = pd.DataFrame(portfolio_analysis).T
231
results_df[['cagr', 'sharpe', 'max_dd', 'calmar']].round(3)
232
```
233
234
### Data Processing Pipeline
235
236
```python
237
import ffn
238
239
# Complex data processing using method chaining
240
raw_data = ffn.get(['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA'], start='2020-01-01')
241
242
# Multi-step processing pipeline
243
processed_data = (raw_data
244
.drop_duplicate_cols() # Remove any duplicate columns
245
.dropna() # Remove NaN values
246
.rebase(100) # Normalize to 100
247
.to_monthly(method='last') # Convert to monthly
248
.to_returns() # Calculate returns
249
.winsorize(limits=0.02) # Winsorize extreme values
250
.dropna() # Clean again
251
)
252
253
print(f"Processed data shape: {processed_data.shape}")
254
print(f"Monthly return statistics:")
255
print(processed_data.describe().round(4))
256
257
# Risk analysis on processed data
258
risk_analysis = {
259
'Sharpe Ratios': processed_data.calc_sharpe(rf=0.02),
260
'Correlations': processed_data.corr(),
261
'Drawdowns': processed_data.to_price_index().to_drawdown_series()
262
}
263
264
# Visualization using extensions
265
processed_data.corr().plot_corr_heatmap(title='Tech Stock Correlations (Monthly)',
266
figsize=(8, 6))
267
268
# Advanced statistical analysis
269
clusters = processed_data.calc_clusters(n=2, plot=True)
270
print(f"Asset clusters: {clusters}")
271
```
272
273
### Format and Display Pipeline
274
275
```python
276
import ffn
277
278
# Get data and calculate statistics
279
prices = ffn.get(['SPY', 'QQQ', 'BND'], start='2020-01-01')
280
returns = prices.to_returns().dropna()
281
282
# Calculate key metrics using extensions
283
metrics = pd.DataFrame({
284
'CAGR': [prices[col].calc_cagr() for col in prices.columns],
285
'Volatility': returns.std() * np.sqrt(252),
286
'Sharpe': returns.calc_sharpe(rf=0.02),
287
'Max Drawdown': [prices[col].calc_max_drawdown() for col in prices.columns],
288
'Calmar': [prices[col].calc_calmar_ratio() for col in prices.columns]
289
}, index=prices.columns)
290
291
# Format for presentation using extensions
292
print("Formatted Performance Summary:")
293
print("CAGR and Max Drawdown (as percentages):")
294
print(metrics[['CAGR', 'Max Drawdown']].as_percent())
295
296
print("\nAll metrics (2 decimal places):")
297
print(metrics.as_format('.3f'))
298
299
# Create formatted correlation matrix
300
correlations = returns.corr()
301
print("\nCorrelation Matrix (as percentages):")
302
print(correlations.as_format('.1%'))
303
```
304
305
## Implementation Notes
306
307
### Automatic Extension
308
- Extensions are applied automatically when `import ffn` is executed
309
- No additional setup required - all methods immediately available on pandas objects
310
- Extensions work with both pandas Series and DataFrame objects where applicable
311
312
### Method Chaining Benefits
313
- Enables functional programming style for financial analysis
314
- Reduces intermediate variable creation
315
- Improves code readability and maintainability
316
- Supports complex multi-step analysis pipelines
317
318
### Performance Considerations
319
- Extensions maintain pandas performance characteristics
320
- No overhead beyond normal function calls
321
- Methods preserve pandas indexing and data alignment behavior
322
- Compatible with pandas operations and other libraries