├── setup.py ├── LICENSE ├── README.md └── quiverquant.py /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r", encoding="utf-8") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="quiverquant", 8 | version="0.1.55", 9 | author="Chris Kardatzke", 10 | author_email="chris@quiverquant.com", 11 | description="Quiver Quantitative Alternative Data", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/Quiver-Quantitative/python-api", 15 | py_modules = ["quiverquant"], 16 | install_requires=[ 17 | 'pandas', 18 | 'requests' 19 | ], 20 | classifiers=[ 21 | "Programming Language :: Python :: 3", 22 | "License :: OSI Approved :: MIT License", 23 | "Operating System :: OS Independent", 24 | ], 25 | python_requires='>=3.6', 26 | ) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quiver Quantitative Alternative Data 2 | This package allows you to access several alternative data sources which are updated daily and mapped to tickers. These include: 3 | - Trading by US congressmen 4 | - Corporate Lobbying 5 | - Government Contracts 6 | - Patents 7 | - Off-exchange short volume 8 | - Companies' Wikipedia page views 9 | - Discussion on Reddit's r/wallstreetbets 10 | - Discussion on Reddit's r/SPACs 11 | - Companies' Twitter followings 12 | - Flights by corporate private jets 13 | - Political Beta 14 | - Insider Transactions 15 | 16 | This data can be used for backtesting and implementing trading strategies. 17 | 18 | ### Receiving API Token 19 | You can sign up for a Quiver API token [here](https://api.quiverquant.com). 20 | 21 | The pricing starts at $10/month, please [e-mail me](mailto:chris@quiverquant.com) if that is an issue and I may be able to help cover. 22 | 23 | ## Getting Started 24 | #### Prerequisites 25 | - Python version 3 installed locally 26 | - Pip installed locally 27 | 28 | #### Installation 29 | The package can easily be installed in your terminal by entering 30 | ```python 31 | pip install quiverquant 32 | ``` 33 | 34 | ### Usage 35 | ```python 36 | #Import the package 37 | import quiverquant 38 | 39 | #Connect to the API using your token 40 | #Replace with your token 41 | quiver = quiverquant.quiver("") 42 | 43 | #Get data on WallStreetBets discussion 44 | dfWSB = quiver.wallstreetbets() 45 | 46 | #Get data on WallStreetBets discussion of GameStop 47 | dfWSB_GameStop = quiver.wallstreetbets("GME") 48 | 49 | #Get recent trades by members of U.S. Congress 50 | dfCongress = quiver.congress_trading() 51 | 52 | #Get trades of a Tesla by members of congress 53 | dfCongress_Tesla = quiver.congress_trading("TSLA") 54 | 55 | #Get trades made by U.S. Senator Richard Burr 56 | dfCongress_Burr = quiver.congress_trading("Richard Burr", politician=True) 57 | 58 | #Get recent corporate lobbying 59 | dfLobbying = quiver.lobbying() 60 | 61 | #Get corporate lobbying by Apple 62 | dfLobbying_Apple = quiver.lobbying("AAPL") 63 | 64 | #Get data on government contracts 65 | dfContracts = quiver.gov_contracts() 66 | 67 | #Get data on government contracts to Lockheed Martin 68 | dfContracts_Lockheed = quiver.gov_contracts("LMT") 69 | 70 | #Get data on off-exchange short volume 71 | dfOTC = quiver.offexchange() 72 | 73 | #Get data on off-exchange short volume for AMC 74 | dfOTC_AMC = quiver.offexchange("AMC") 75 | 76 | #Get data on Wikipedia page views 77 | dfWiki = quiver.wikipedia() 78 | 79 | #Get data on Wikipedia page views of Microsoft 80 | dfWiki_Microsoft = quiver.wikipedia("MSFT") 81 | 82 | #Get data on companies' Twitter following 83 | dfTwitter = quiver.twitter() 84 | 85 | #Get data on GE's Twitter following 86 | dfTwitter_GE = quiver.twitter("GE") 87 | 88 | #Get data on r/SPACs discussion 89 | dfSPACs = quiver.spacs() 90 | 91 | #Get data on r/SPACs discussion of CCIV 92 | dfSPACs_CCIV = quiver.spacs("CCIV") 93 | 94 | #Get data on recent corporate private jet flights 95 | dfFlights = quiver.flights() 96 | 97 | #Get data on private jet flights by Target 98 | dfFlights_Target = quiver.flights("TGT") 99 | 100 | #Get data on patents by Apple 101 | dfPatents_Apple = quiver.patents("AAPL") 102 | 103 | #Get data on recent insider transactions 104 | dfInsiders = quiver.insiders() 105 | 106 | #Get data on recent insider transactions by Tesla insiders 107 | dfInsiders_Tesla = quiver.insiders("TSLA") 108 | ``` 109 | 110 | 111 | -------------------------------------------------------------------------------- /quiverquant.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import pandas as pd 4 | 5 | class quiver: 6 | def __init__(self, token): 7 | self.token = token 8 | self.headers = {'accept': 'application/json', 9 | 'X-CSRFToken': 'TyTJwjuEC7VV7mOqZ622haRaaUr0x0Ng4nrwSRFKQs7vdoBcJlK9qjAS69ghzhFu', 10 | 'Authorization': "Token "+self.token} 11 | 12 | def congress_trading(self, ticker="", politician=False, recent=True): 13 | if recent: 14 | urlStart = 'https://api.quiverquant.com/beta/live/congresstrading' 15 | else: 16 | urlStart = 'https://api.quiverquant.com/beta/bulk/congresstrading' 17 | if politician: 18 | ticker = ticker.replace(" ", "%20") 19 | url = urlStart+"?representative="+ticker 20 | 21 | elif len(ticker)>0: 22 | url = urlStart+"/"+ticker 23 | else: 24 | url = urlStart 25 | print(url) 26 | r = requests.get(url, headers=self.headers) 27 | j = json.loads(r.content) 28 | df = pd.DataFrame(j) 29 | df["ReportDate"] = pd.to_datetime(df["ReportDate"]) 30 | df["TransactionDate"] = pd.to_datetime(df["TransactionDate"]) 31 | return df 32 | 33 | 34 | def senate_trading(self, ticker=""): 35 | if len(ticker)>0: 36 | url = "https://api.quiverquant.com/beta/historical/senatetrading/"+ticker 37 | else: 38 | url = "https://api.quiverquant.com/beta/live/senatetrading" 39 | r = requests.get(url, headers=self.headers) 40 | j = json.loads(r.content) 41 | df = pd.DataFrame(j) 42 | df["Date"] = pd.to_datetime(df["Date"]) 43 | return df 44 | 45 | def house_trading(self, ticker=""): 46 | if len(ticker)>0: 47 | url = "https://api.quiverquant.com/beta/historical/housetrading/"+ticker 48 | else: 49 | url = "https://api.quiverquant.com/beta/live/housetrading" 50 | r = requests.get(url, headers=self.headers) 51 | j = json.loads(r.content) 52 | df = pd.DataFrame(j) 53 | df["Date"] = pd.to_datetime(df["Date"]) 54 | return df 55 | 56 | def offexchange(self, ticker=""): 57 | if len(ticker)>0: 58 | url = "https://api.quiverquant.com/beta/historical/offexchange/"+ticker 59 | else: 60 | url = "https://api.quiverquant.com/beta/live/offexchange" 61 | r = requests.get(url, headers=self.headers) 62 | j = json.loads(r.content) 63 | df = pd.DataFrame(j) 64 | 65 | if len(ticker)>0: 66 | df["Date"] = pd.to_datetime(df["Date"]) 67 | 68 | return df 69 | 70 | def gov_contracts(self, ticker=""): 71 | if len(ticker)>0: 72 | url = "https://api.quiverquant.com/beta/historical/govcontractsall/"+ticker 73 | else: 74 | url = "https://api.quiverquant.com/beta/live/govcontractsall" 75 | 76 | r = requests.get(url, headers=self.headers) 77 | df = pd.DataFrame(json.loads(r.content)) 78 | return df 79 | 80 | 81 | def lobbying(self, ticker=""): 82 | if len(ticker)>0: 83 | url = "https://api.quiverquant.com/beta/historical/lobbying/"+ticker 84 | else: 85 | url = "https://api.quiverquant.com/beta/live/lobbying" 86 | 87 | r = requests.get(url, headers=self.headers) 88 | df = pd.DataFrame(json.loads(r.content)) 89 | df["Date"] = pd.to_datetime(df["Date"]) 90 | return df 91 | 92 | def insiders(self, ticker=""): 93 | if len(ticker)>0: 94 | url = "https://api.quiverquant.com/beta/live/insiders?ticker="+ticker 95 | else: 96 | url = "https://api.quiverquant.com/beta/live/insiders" 97 | 98 | print("Drawing from: ", url) 99 | r = requests.get(url, headers=self.headers) 100 | df = pd.DataFrame(json.loads(r.content)) 101 | # df["Date"] = pd.to_datetime(df["Date"]) 102 | return df 103 | 104 | 105 | 106 | def wikipedia(self, ticker=""): 107 | if len(ticker)>0: 108 | url = "https://api.quiverquant.com/beta/historical/wikipedia/"+ticker 109 | else: 110 | url = "https://api.quiverquant.com/beta/live/wikipedia" 111 | 112 | r = requests.get(url, headers=self.headers) 113 | 114 | if r.text == '"Upgrade your subscription plan to access this dataset."': 115 | raise NameError('Upgrade your subscription plan to access this dataset.') 116 | 117 | df = pd.DataFrame(json.loads(r.content)) 118 | return df 119 | 120 | def wallstreetbets(self, ticker="",date_from = "", date_to = "", yesterday=False): 121 | if len(ticker)>0: 122 | url = "https://api.quiverquant.com/beta/historical/wallstreetbets/"+ticker 123 | 124 | else: 125 | url = "https://api.quiverquant.com/beta/live/wallstreetbets?count_all=true" 126 | 127 | if len(date_from)>0: 128 | date_from = pd.to_datetime(date_from).strftime('%Y%m%d') 129 | url = url+"&date_from="+date_from 130 | if len(date_to)>0: 131 | url = url+"&date_to="+date_to 132 | 133 | if yesterday: 134 | url = "https://api.quiverquant.com/beta/live/wallstreetbets" 135 | 136 | print(url) 137 | r = requests.get(url, headers=self.headers) 138 | 139 | if r.text == '"Upgrade your subscription plan to access this dataset."': 140 | raise NameError('Upgrade your subscription plan to access this dataset.') 141 | 142 | df = pd.DataFrame(json.loads(r.content)) 143 | 144 | if not yesterday: 145 | try: 146 | df["Date"] = pd.to_datetime(df["Time"], unit='ms') 147 | except: 148 | df["Date"] = pd.to_datetime(df["Date"]) 149 | if len(date_from)>0: 150 | df = df[df["Date"]>=pd.to_datetime(date_from)] 151 | if len(date_to)>0: 152 | df = df[df["Date"]<=pd.to_datetime(date_to)] 153 | 154 | return df 155 | 156 | def twitter(self, ticker = ""): 157 | if len(ticker)>0: 158 | url = "https://api.quiverquant.com/beta/historical/twitter/"+ticker 159 | else: 160 | url = "https://api.quiverquant.com/beta/live/twitter" 161 | 162 | r = requests.get(url, headers=self.headers) 163 | 164 | if r.text == '"Upgrade your subscription plan to access this dataset."': 165 | raise NameError('Upgrade your subscription plan to access this dataset.') 166 | 167 | df = pd.DataFrame(json.loads(r.content)) 168 | return df 169 | 170 | def spacs(self, ticker = ""): 171 | if len(ticker)>0: 172 | url = "https://api.quiverquant.com/beta/historical/spacs/"+ticker 173 | else: 174 | url = "https://api.quiverquant.com/beta/live/spacs" 175 | 176 | r = requests.get(url, headers=self.headers) 177 | 178 | if r.text == '"Upgrade your subscription plan to access this dataset."': 179 | raise NameError('Upgrade your subscription plan to access this dataset.') 180 | 181 | df = pd.DataFrame(json.loads(r.content)) 182 | return df 183 | 184 | def flights(self, ticker = ""): 185 | if len(ticker)>0: 186 | url = "https://api.quiverquant.com/beta/historical/flights/"+ticker 187 | else: 188 | url = "https://api.quiverquant.com/beta/live/flights" 189 | 190 | r = requests.get(url, headers=self.headers) 191 | 192 | if r.text == '"Upgrade your subscription plan to access this dataset."': 193 | raise NameError('Upgrade your subscription plan to access this dataset.') 194 | 195 | df = pd.DataFrame(json.loads(r.content)) 196 | return df 197 | 198 | 199 | def political_beta(self, ticker = ""): 200 | if len(ticker)>0: 201 | url = "https://api.quiverquant.com/beta/historical/politicalbeta/"+ticker 202 | else: 203 | url = "https://api.quiverquant.com/beta/live/politicalbeta" 204 | 205 | r = requests.get(url, headers=self.headers) 206 | 207 | if r.text == '"Upgrade your subscription plan to access this dataset."': 208 | raise NameError('Upgrade your subscription plan to access this dataset.') 209 | 210 | df = pd.DataFrame(json.loads(r.content)) 211 | return df 212 | 213 | def patents(self, ticker = ""): 214 | if len(ticker)<1: 215 | url = "https://api.quiverquant.com/beta/live/allpatents" 216 | else: 217 | url = "https://api.quiverquant.com/beta/historical/allpatents/"+ticker 218 | 219 | print("Pulling data from: ", url) 220 | r = requests.get(url, headers=self.headers) 221 | 222 | if r.text == '"Upgrade your subscription plan to access this dataset."': 223 | raise NameError('Upgrade your subscription plan to access this dataset.') 224 | 225 | df = pd.DataFrame(json.loads(r.content)) 226 | df["Date"] = pd.to_datetime(df["Date"]) 227 | 228 | return df 229 | 230 | ## Contact chris@quiverquant.com about access to these functions 231 | def sec13F(self, ticker="", date="", owner="", period=""): 232 | separator = "?" 233 | url = "https://api.quiverquant.com/beta/live/sec13f" 234 | if len(ticker)>0: 235 | url = url+separator+"ticker="+ticker 236 | separator = "&" 237 | if len(date)>0: 238 | url = url+separator+"date="+date 239 | separator = "&" 240 | if len(owner)>0: 241 | url = url+separator+"owner="+owner 242 | separator="&" 243 | if len(period)>0: 244 | url = url+separator+"period="+period 245 | separator="&" 246 | 247 | print("Pulling data from: ", url) 248 | r = requests.get(url, headers=self.headers) 249 | 250 | if r.text == '"Upgrade your subscription plan to access this dataset."': 251 | raise NameError('Upgrade your subscription plan to access this dataset.') 252 | 253 | df = pd.DataFrame(json.loads(r.content)) 254 | df["ReportPeriod"] = pd.to_datetime(df["ReportPeriod"], unit='ms') 255 | # #Can edit this out once we get past May 25th 256 | # values = [] 257 | # for val in df["Value"]: 258 | # if len(val)>=900: 259 | # values.append(val[:len(val)//1000]) 260 | # else: 261 | # values.append(val) 262 | # df["Value"] = values 263 | ######## 264 | 265 | # df["Value"] = df["Value"].str.replace(",", "") 266 | # df["Value"] = df["Value"].astype(float) 267 | 268 | # df["Shares"] = df["Shares"].str.replace(",", "") 269 | # df["Shares"] = df["Shares"].astype(float) 270 | df["Date"] = pd.to_datetime(df["Date"], unit="ms") 271 | return df 272 | 273 | def sec13FChanges(self, ticker="", date="", owner="", period=""): 274 | separator = "?" 275 | url = "https://api.quiverquant.com/beta/live/sec13fchanges" 276 | if len(ticker)>0: 277 | url = url+separator+"ticker="+ticker 278 | separator = "&" 279 | if len(date)>0: 280 | url = url+separator+"date="+date 281 | separator = "&" 282 | if len(owner)>0: 283 | url = url+separator+"owner="+owner 284 | separator="&" 285 | if len(period)>0: 286 | url = url+separator+"period="+period 287 | separator="&" 288 | 289 | print("Pulling data from: ", url) 290 | r = requests.get(url, headers=self.headers) 291 | 292 | if r.text == '"Upgrade your subscription plan to access this dataset."': 293 | raise NameError('Upgrade your subscription plan to access this dataset.') 294 | 295 | df = pd.DataFrame(json.loads(r.content)) 296 | df["ReportPeriod"] = pd.to_datetime(df["ReportPeriod"], unit='ms') 297 | # #Can edit this out once we get past May 25th 298 | # values = [] 299 | # for val in df["Value"]: 300 | # if len(val)>=900: 301 | # values.append(val[:len(val)//1000]) 302 | # else: 303 | # values.append(val) 304 | # df["Value"] = values 305 | ######## 306 | 307 | # df["Value"] = df["Value"].str.replace(",", "") 308 | # df["Value"] = df["Value"].astype(float) 309 | 310 | # df["Shares"] = df["Shares"].str.replace(",", "") 311 | # df["Shares"] = df["Shares"].astype(float) 312 | df["Date"] = pd.to_datetime(df["Date"], unit="ms") 313 | return df 314 | 315 | def wallstreetbetsComments(self, ticker="", freq="", date_from = "", date_to = ""): 316 | separator = "?" 317 | url = "https://api.quiverquant.com/beta/live/wsbcomments" 318 | if len(ticker)>0: 319 | url = url+separator+"ticker="+ticker 320 | separator = "&" 321 | if len(freq)>0: 322 | url = url+separator+"freq="+freq 323 | separator = "&" 324 | if len(date_from)>0: 325 | url = url+separator+"date_from="+date_from 326 | separator = "&" 327 | if len(date_to)>0: 328 | url = url+separator+"date_to="+date_to 329 | separator = "&" 330 | 331 | print("Pulling data from: ", url) 332 | r = requests.get(url, headers=self.headers) 333 | 334 | if r.text == '"Upgrade your subscription plan to access this dataset."': 335 | raise NameError('Upgrade your subscription plan to access this dataset. Contact chris@quiverquant.com with questions.') 336 | 337 | df = pd.DataFrame(json.loads(r.content)) 338 | df['Datetime'] = pd.to_datetime(df["Time"], unit='ms') 339 | return df 340 | 341 | def wallstreetbetsCommentsFull(self, ticker="", freq="", date_from = "", date_to = ""): 342 | separator = "?" 343 | url = "https://api.quiverquant.com/beta/live/wsbcommentsfull" 344 | if len(ticker)>0: 345 | url = url+separator+"ticker="+ticker 346 | separator = "&" 347 | if len(freq)>0: 348 | url = url+separator+"freq="+freq 349 | separator = "&" 350 | if len(date_from)>0: 351 | url = url+separator+"date_from="+date_from 352 | separator = "&" 353 | if len(date_to)>0: 354 | url = url+separator+"date_to="+date_to 355 | separator = "&" 356 | 357 | print("Pulling data from: ", url) 358 | r = requests.get(url, headers=self.headers) 359 | 360 | if r.text == '"Upgrade your subscription plan to access this dataset."': 361 | raise NameError('Upgrade your subscription plan to access this dataset. Contact chris@quiverquant.com with questions.') 362 | 363 | df = pd.DataFrame(json.loads(r.content)) 364 | df['Datetime'] = pd.to_datetime(df["Time"], unit='ms') 365 | return df 366 | 367 | 368 | def cryptoComments(self, ticker="", freq="", date_from = "", date_to = ""): 369 | separator = "?" 370 | url = "https://api.quiverquant.com/beta/live/cryptocomments" 371 | if len(ticker)>0: 372 | url = url+separator+"ticker="+ticker 373 | separator = "&" 374 | if len(freq)>0: 375 | url = url+separator+"freq="+freq 376 | separator = "&" 377 | if len(date_from)>0: 378 | url = url+separator+"date_from="+date_from 379 | separator = "&" 380 | if len(date_to)>0: 381 | url = url+separator+"date_to="+date_to 382 | separator = "&" 383 | 384 | print("Pulling data from: ", url) 385 | r = requests.get(url, headers=self.headers) 386 | 387 | if r.text == '"Upgrade your subscription plan to access this dataset."': 388 | raise NameError('Upgrade your subscription plan to access this dataset. Contact chris@quiverquant.com with questions.') 389 | 390 | df = pd.DataFrame(json.loads(r.content)) 391 | df['Datetime'] = pd.to_datetime(df["Time"], unit='ms') 392 | return df 393 | 394 | def cryptoCommentsHistorical(self, ticker="", freq="", date_from = "", date_to = ""): 395 | separator = "?" 396 | url = "https://api.quiverquant.com/beta/live/cryptocommentsfull" 397 | if len(ticker)>0: 398 | url = url+separator+"ticker="+ticker 399 | separator = "&" 400 | if len(freq)>0: 401 | url = url+separator+"freq="+freq 402 | separator = "&" 403 | if len(date_from)>0: 404 | url = url+separator+"date_from="+date_from 405 | separator = "&" 406 | if len(date_to)>0: 407 | url = url+separator+"date_to="+date_to 408 | separator = "&" 409 | 410 | print("Pulling data from: ", url) 411 | r = requests.get(url, headers=self.headers) 412 | 413 | if r.text == '"Upgrade your subscription plan to access this dataset."': 414 | raise NameError('Upgrade your subscription plan to access this dataset. Contact chris@quiverquant.com with questions.') 415 | 416 | df = pd.DataFrame(json.loads(r.content)) 417 | df['Datetime'] = pd.to_datetime(df["Time"], unit='ms') 418 | return df 419 | 420 | 421 | 422 | --------------------------------------------------------------------------------