Package: pyfinance
pyfinancepyfinance is a Python package built for investment management and analysis of security returns.It is meant to be a complement to existing packages geared towards quantitative finance, such aspyfolio,pandas-datareader, andfecon235.SupportsPython 3.5 | 3.6 | 3.7 | 3.8 | 3.9Latest ReleasePackage StatusLicenseContentspyfinance is best explored on a module-by-module basis:ModuleDescriptiondatasets.pyFinancial dataset download & assembly viarequests.general.pyGeneral-purpose financial computations, such as active share calculation, returns distribution approximation, and tracking error optimization.ols.pyOrdinary least-squares (OLS) regression, supporting static and rolling cases, built with a matrix formulation and implemented with NumPy.options.pyVectorized option calculations, including Black-Scholes Merton European option valuation, Greeks, and implied volatility, as well as payoff determination for common money-spread option strategies.returns.pyStatistical analysis of financial time series through the CAPM framework, designed to mimic functionality of software such as FactSet Research Systems and Zephyr, with improved speed and flexibility.utils.pyUtilities not fitting into any of the above.Please note thatreturnsandgeneralare still in development; they are not thoroughly tested and have some NotImplemented features.Installationpyfinance is available viaPyPI.  Install with pip:$python3-minstallpyfinanceNote: pyfinance aims for compatibility with all minor releases of Python 3.x, but does not guarantee workability with Python 2.x.Dependenciespyfinance relies primarily on Python's scientific stack, including NumPy, Pandas, Matplotlib, Seaborn, Scikit-Learn, and StatsModels.  Other dependencies include Beautiful Soup, Requests, xrld, and xmltodict.Seesetup.pyfor specific version threshold requirements.TutorialThis is a walkthrough of some of pyfinance's features.Thereturns.pymodule is designed for statistical analysis of financial time series through the CAPM framework, designed to mimic functionality of software such as FactSet Research Systems and Zephyr, with improved speed and flexibility.Its main class isTSeries, a subclassed Pandas Series.  The DataFrame equivalent,TFrame, is not yet implemented as of March 2018.TSeriesimplements a collection of new methods that pertain specifically to investment management and the study of security returns and asset performance, such cumulative return indices and drawdown.Here's an example of construction:>>>importnumpyasnp>>>importpandasaspd>>>frompyfinanceimportTSeries>>>np.random.seed(444)# Normally distributed with 0.08% daily drift term.>>>s=np.random.randn(400)/100+0.0008>>>idx=pd.date_range(start='2016',periods=len(s))# default daily freq.>>>ts=TSeries(s,index=idx)>>>ts.head()2016-01-010.00442016-01-020.00462016-01-030.01462016-01-040.01262016-01-05-0.0086Freq:D,dtype:float64And a few "new" methods:>>>ts.max_drawdown()-0.12374551561531844# Downsample to quarterly compounded returns.>>>ts.rollup('Q')2016-03-310.04502016-06-300.12402016-09-300.06312016-12-31-0.00812017-03-310.1925Freq:Q-DEC,dtype:float64>>>ts.anlzd_stdev()0.16318780660107757>>>ts.sharpe_ratio(ddof=1)2.501797257311737Some statistics are benchmark-relative.  For methods that take abenchmarkparameter,benchmarkcan be either anotherTSeries, a Pandas Series, a 1d NumPy array.>>>bmk=TSeries(np.random.randn(400)/100+.0005,...index=ts.index)>>>ts.beta_adj(bmk)0.3176455956603447>>>ts.tracking_error(benchmark=bmk)0.23506660057562254With CAPM-related statistics such as alpha, beta, and R-squared, it can also be a Pandas DataFrame or 2d NumPy array.>>>multi_bmk=pd.DataFrame(np.random.randn(400,2)/100+.0005,...index=ts.index)>>># Multifactor model support.>>>ts.alpha(multi_bmk)0.0010849614688207107TSeriescomes with just one additional and optional argument that must be as a keyword argument:freq(defaultNone) allows for manual specification of the time-series frequency.  It may be any frequency string or anchored offset string recognized by Pandas, such as 'D', '5D', 'Q', 'Q-DEC', or 'BQS-APR'.>>># This is okay as long as a frequency can be inferred.>>>ts.freqisNoneTrueThe purpose of this extra parameter is to create an annualization factor for statistics that are given on an annualized basis, such as standard deviation.If no frequency is passed explicitly, pyfinance will attempt to infer an annualization factor from the Index, with an exception being raised if neither of these yield a frequency.>>>no_idx=TSeries(np.random.laplace(size=24)*.01+.005,freq='M')>>>no_idx.freq'M'>>>no_idx.anlzd_ret()0.04975219957136123freqcan also be passed within some methods, which will override the class instance's.freqif it exists:>>>no_idx.anlzd_ret(freq='W')# Treat `no_idx` as weekly returns.0.2341731795205313datasets.pyprovides for financial dataset download & assembly viarequests.  It leverages sources including:Ken French's data library (viapandas-datareader);SEC.gov;cboe.com;AQR's dataset page;fred.stlouisfed.org;Robert Shiller's page at econ.yale.edu.Below is a batch of examples.Load SEC 13F filings:# Third Point LLC June 2017 13F>>>frompyfinanceimportdatasets>>>url='https://www.sec.gov/Archives/edgar/data/1040273/000108514617001787/form13fInfoTable.xml'# noqa>>>df=datasets.load_13f(url=url)>>>df.head()nameOfIssuertitleOfClasscusipvaluevotingAuthority0ALEXIONPHARMACE...COM01535110915208812500001ALIBABAGROUPHL...SPONSOREDADS01609W10263405045000002ALPHABETINCCAPSTKCLA02079K3055345665750003ANTHEMINCCOM03675210323516212500004BANCOMACROSASPONADRB05961W10582971900000Industry-portfolio monthly returns:>>>frompyfinanceimportdatasets>>>ind=datasets.load_industries()>>>ind.keys()dict_keys([5,10,12,17,30,38,48])# Monthly returns to 5 industry portfolios>>>ind[5].head()CnsmrManufHiTecHlthOtherDate1950-01-311.261.473.211.063.191950-02-281.911.292.061.921.021950-03-310.281.933.46-2.90-0.681950-04-303.225.213.585.521.501950-05-313.816.181.073.961.36S&P 500 and interest rate data from Robert Shiller's website, 1871-present:>>>frompyfinanceimportdatasets>>>shiller=datasets.load_shiller()>>>shiller.iloc[:7,:5]sp50psp50dsp50ecpireal_ratedate1871-01-314.440.260.412.46415.32001871-02-284.500.260.412.84465.32331871-03-314.610.260.413.03505.32671871-04-304.740.260.412.55925.33001871-05-314.860.260.412.27385.33331871-06-304.820.260.412.08355.33671871-07-314.730.260.412.08355.3400Theols.pymodule provides ordinary least-squares (OLS) regression, supporting static and rolling cases, and is built with a matrix formulation and implemented with NumPy.First, let's load some data on currencies, interest rates, and commodities to generate a regression of changes in the trade-weighted USD against interest rate term spreads and copper.>>>frompandas_datareaderimportDataReader>>>syms={...'TWEXBMTH':'usd',...'T10Y2YM':'term_spread',...'PCOPPUSDM':'copper'...}>>>data=DataReader(syms.keys(),data_source='fred',...start='2000-01-01',end='2016-12-31')\....pct_change()\....dropna()\....rename(columns=syms)>>>y=data.pop('usd')>>>data.head()term_spreadcopperDATE2000-02-01-1.4091-0.02002000-03-012.0000-0.03722000-04-010.5185-0.03332000-05-01-0.09760.06142000-06-010.0270-0.0185>>>y.head()DATE2000-02-010.01262000-03-01-0.00012000-04-010.00562000-05-010.02202000-06-01-0.0101TheOLSclass implements "static" (single) linear regression, with the model being fit when the object is instantiated.It is designed primarily for statistical inference, not out-of-sample prediction, and its attributes largely mimic the structure of StatsModels'RegressionResultsWrapper.>>>frompyfinanceimportols>>>model=ols.OLS(y=y,x=data)>>>model.alpha# the intercept - a scalar0.0012303204434167458>>>model.beta# the coefficientsarray([-0.0006,-0.0949])>>>model.fstat33.42923069295481# Residuals and predicted y values are NumPy arrays# with the same shape as `y`.>>>model.resids.shape(203,)>>>model.predicted.shape(203,)The module also supports rolling regression.  (Iterative regressions done on sliding windows over the data.)RollingOLShas methods that generate NumPy arrays as outputs.PandasRollingOLSis a wrapper aroundRollingOLSand is meant to mimic the look of Pandas's deprecatedMovingOLSclass.  It generates Pandas DataFrame and Series outputs.Note: all solutions are generated through a matrix formulation, which takes advantage of NumPy's broadcasting capabilities to expand the classicalmatrix formulationto an additional dimension.  This approach may be slow for significantly large datasets.Also, note that windows are not "time-aware" in the way that Pandas time functionality is.  Because of the NumPy implementation, specifying a window of 12 where the index contains one missing months would generate a regression over 13 months.  To avoid this, simply reindex the input data to a set frequency.# 12-month rolling regressions# First entry would be the "12 months ending" 2001-01-30>>>rolling=ols.PandasRollingOLS(y=y,x=data,window=12)>>>rolling.beta.head()term_spreadcopperDATE2001-01-019.9127e-050.05562001-02-014.7607e-040.06272001-03-011.4671e-030.03572001-04-011.6101e-030.02962001-05-011.5839e-03-0.0449>>>rolling.alpha.head()DATE2001-01-010.00552001-02-010.00502001-03-010.00672001-04-010.00702001-05-010.0048>>>rolling.pvalue_alpha.head()DATE2001-01-010.09962001-02-010.11012001-03-010.05552001-04-010.04792001-05-010.1020options.pyis built for vectorized options calculations.BSMencapsulates a European option and its associated value, Greeks, and implied volatility, using the Black-Scholes Merton model.>>>frompyfinance.optionsimportBSM>>>op=BSM(S0=100,K=100,T=1,r=.04,sigma=.2)>>>op.summary()OrderedDict([('Value',9.925053717274437),('d1',0.3),('d2',0.09999999999999998),('Delta',0.6179114221889526),('Gamma',0.019069390773026208),('Vega',38.138781546052414),('Theta',-5.888521694670074),('Rho',51.86608850162082),('Omega',6.225774084360724)])# What is the implied annualized volatility at P=10?>>>op.implied_vol(value=10)0.20196480875586834# Vectorized - pass an array of strikes.>>>importnumpyasnp>>>ops=BSM(S0=100,K=np.arange(100,110),T=1,r=.04,sigma=.2)>>>ops.value()array([9.9251,9.4159,8.9257,8.4543,8.0015,7.567,7.1506,6.7519,6.3706,6.0064])# Multiple array inputs are evaluated elementwise/zipped.>>>ops2=BSM(S0=np.arange(100,110),K=np.arange(100,110),...T=1,r=.04,sigma=.2)>>>ops2BSM(kind=call,S0=[100101102103104105106107108109],K=[100101102103104105106107108109],T=1,r=0.04,sigma=0.2)>>>ops2.value()array([9.9251,10.0243,10.1236,10.2228,10.3221,10.4213,10.5206,10.6198,10.7191,10.8183])options.pyalso exports a handful of optionsstrategies, such asStraddle,Straddle,Strangle,BullSpread, andShortButterfly, to name a few.All of these inherit from a generic and customizableOpStratclass, which can be built from an arbitrary number of puts and/or calls.Here is an example of constructing a bear spread, which is a combination of 2 puts or 2 calls (putis the default).  Here, we are short a put at 1950 and long a put at 2050.  Like the case of a single option, the instance methods are vectorized, so we can compute payoff and profit across a vector or grid:>>>frompyfinanceimportoptionsasop>>>spread=op.BearSpread(St=np.array([2100,2000,1900]),...K1=1950.,K2=2050.,...price1=56.01,price2=107.39)>>>spread.payoff()array([0.,50.,100.])>>>spread.profit()array([-51.38,-1.38,48.62])Theutils.pymodule contains odds-and-ends utilities.>>>frompyfinanceimportutils# Generate 7 unique 5-letter mutual fund tickers>>>utils.random_tickers(length=5,n_tickers=7,endswith='X')['JXNQX','DPTJX','WAKOX','DZIHX','MDYXX','HSKWX','IDMZX']# Same for ETFs>>>utils.random_tickers(3,8)['FIS','FNN','FZC','PWV','PBA','RDG','BKY','CDW']# Five-asset portfolio leveraged 1.5x.>>>utils.random_weights(size=5,sumto=1.5)array([0.3263,0.1763,0.4703,0.4722,0.0549])# Two 7-asset portfolios leverage 1.0x and 1.5x, respectively.>>>utils.random_weights(size=(2,7),sumto=[1.,1.5])array([[0.1418,0.2007,0.0255,0.2575,0.0929,0.2272,0.0544],[0.3041,0.109,0.2561,0.2458,0.3001,0.0333,0.2516]])>>>utils.random_weights(size=(2,7),sumto=[1.,1.5]).sum(axis=1)array([1.,1.5])# Convert Pandas offset alises to periods per year.>>>frompyfinanceimportutils>>>utils.get_anlz_factor('M')12.0>>>utils.get_anlz_factor('BQS-DEC')4.0APIFor in-depth call syntaxes, see the source docstrings.