├── README.md ├── backend ├── api.py ├── constants.py ├── nlp.py └── service.py └── webapp ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.css ├── App.js ├── App.test.js ├── components ├── Chart │ ├── Chart.css │ └── Chart.js ├── Loader │ ├── Loader.css │ └── Loader.js ├── TwitterFeed │ ├── TwitterFeed.css │ └── TwitterFeed.js └── User │ ├── User.css │ └── User.js ├── index.css ├── index.js ├── logo.svg ├── pages ├── Home.css ├── Home.js ├── UserPage.css └── UserPage.js ├── reportWebVitals.js └── setupTests.js /README.md: -------------------------------------------------------------------------------- 1 | # TwitterStockMonitor 2 | This project originally started when I noticed that if Trump mentioned a company within his tweets, their shares would either go up in price if the tweet was positive or drop if the tweet was negative. I decided to make a script to follow Trump and monitor the companies that he mentioned over a week, to see how the tweet affected the price of the company's shares. I have updated this to make it usable for multiple Twitter accounts at the same time. 3 | 4 | ## Requirements 5 | For this you will need: 6 | - 7 | - Pinance 8 | - Tweepy 9 | - NLTK 10 | 11 | These can be installed via: 12 | ``` 13 | pip install pinance 14 | pip install tweepy 15 | pip install nltk 16 | ``` 17 | 18 | ## Setup 19 | - You must first set up a [Twitter APP](https://themepacific.com/how-to-generate-api-key-consumer-token-access-key-for-twitter-oauth/994/) 20 | 21 | - You need to set up an email and make sure that your account allows [less secure apps](https://support.google.com/accounts/answer/6010255?hl=en) 22 | - Input this data into the config file with your keys and email info like so: 23 | 24 |  25 | 26 | 27 | This is still being updated. 28 | 29 | 30 | -------------------------------------------------------------------------------- /backend/api.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from flask_cors import CORS 5 | from flask import Flask, jsonify, request 6 | from constants import DIRECTORIES 7 | 8 | app = Flask(__name__) 9 | CORS(app) 10 | 11 | @app.route('/', methods = ['GET']) 12 | def index(): 13 | if request.method == 'GET': 14 | response_obj = { directory: {} for directory in DIRECTORIES} 15 | for directory in DIRECTORIES: 16 | files = os.listdir(f'{os.curdir}/{directory}/') 17 | 18 | for f in files: 19 | name = f.split(".")[0] 20 | with open(f'./{directory}/{f}', 'r') as nf: 21 | response_obj[directory][name] = json.load(nf) 22 | 23 | return response_obj 24 | 25 | return {} 26 | 27 | 28 | 29 | if __name__=='__main__': 30 | app.run(debug=True) -------------------------------------------------------------------------------- /backend/constants.py: -------------------------------------------------------------------------------- 1 | USERNAMES = ['jimcramer'] # 'LizAnnSonders', 'morganhousel', 'charliebilello' 2 | DIRECTORIES = ['historical', 'mentions', 'tweets', 'users'] 3 | COMPANIES = { 4 | "3M":"MMM", 5 | "A. O. Smith":"AOS", 6 | "Abbott":"ABT", 7 | "AbbVie":"ABBV", 8 | "Abiomed":"ABMD", 9 | "Accenture":"ACN", 10 | "Activision Blizzard":"ATVI", 11 | "ADM":"ADM", 12 | "Adobe Inc.":"ADBE", 13 | "ADP":"ADP", 14 | "Advance Auto Parts":"AAP", 15 | "AES Corporation":"AES", 16 | "Aflac":"AFL", 17 | "Agilent Technologies":"A", 18 | "Air Products and Chemicals":"APD", 19 | "Akamai":"AKAM", 20 | "Alaska Air Group":"ALK", 21 | "Albemarle Corporation":"ALB", 22 | "Alexandria Real Estate Equities":"ARE", 23 | "Align Technology":"ALGN", 24 | "Allegion":"ALLE", 25 | "Alliant Energy":"LNT", 26 | "Allstate":"ALL", 27 | "Alphabet Inc. (Class A)":"GOOGL", 28 | "Alphabet Inc. (Class C)":"GOOG", 29 | "Altria":"MO", 30 | "Amazon":"AMZN", 31 | "Amcor":"AMCR", 32 | "AMD":"AMD", 33 | "Ameren":"AEE", 34 | "American Airlines Group":"AAL", 35 | "American Electric Power":"AEP", 36 | "American Express":"AXP", 37 | "American International Group":"AIG", 38 | "American Tower":"AMT", 39 | "American Water Works":"AWK", 40 | "Ameriprise Financial":"AMP", 41 | "AmerisourceBergen":"ABC", 42 | "Ametek":"AME", 43 | "Amgen":"AMGN", 44 | "Amphenol":"APH", 45 | "Analog Devices":"ADI", 46 | "Ansys":"ANSS", 47 | "Anthem":"ANTM", 48 | "Aon":"AON", 49 | "APA Corporation":"APA", 50 | "Apple Inc.":"AAPL", 51 | "Applied Materials":"AMAT", 52 | "Aptiv":"APTV", 53 | "Arista Networks":"ANET", 54 | "Arthur J. Gallagher & Co.":"AJG", 55 | "Assurant":"AIZ", 56 | "AT&T":"T", 57 | "Atmos Energy":"ATO", 58 | "Autodesk":"ADSK", 59 | "AutoZone":"AZO", 60 | "AvalonBay Communities":"AVB", 61 | "Avery Dennison":"AVY", 62 | "Baker Hughes":"BKR", 63 | "Ball Corporation":"BALL", 64 | "Bank of America":"BAC", 65 | "Bath & Body Works":"BBWI", 66 | "Baxter International":"BAX", 67 | "Becton Dickinson":"BDX", 68 | "Berkley":"WRB", 69 | "Berkshire Hathaway":"BRK.B", 70 | "Best Buy":"BBY", 71 | "Bio-Rad":"BIO", 72 | "Bio-Techne":"TECH", 73 | "Biogen":"BIIB", 74 | "BlackRock":"BLK", 75 | "BNY Mellon":"BK", 76 | "Boeing":"BA", 77 | "Booking Holdings":"BKNG", 78 | "BorgWarner":"BWA", 79 | "Boston Properties":"BXP", 80 | "Boston Scientific":"BSX", 81 | "Bristol Myers Squibb":"BMY", 82 | "Broadcom Inc.":"AVGO", 83 | "Broadridge Financial Solutions":"BR", 84 | "Brown & Brown":"BRO", 85 | "Brown–Forman":"BF.B", 86 | "C.H. Robinson":"CHRW", 87 | "Cadence Design Systems":"CDNS", 88 | "Caesars Entertainment":"CZR", 89 | "Camden Property Trust":"CPT", 90 | "Campbell Soup Company":"CPB", 91 | "Capital One":"COF", 92 | "Cardinal Health":"CAH", 93 | "CarMax":"KMX", 94 | "Carnival":"CCL", 95 | "Carrier Global":"CARR", 96 | "Catalent":"CTLT", 97 | "Caterpillar Inc.":"CAT", 98 | "Cboe Global Markets":"CBOE", 99 | "CBRE Group":"CBRE", 100 | "CDW":"CDW", 101 | "Celanese":"CE", 102 | "Centene Corporation":"CNC", 103 | "CenterPoint Energy":"CNP", 104 | "Ceridian":"CDAY", 105 | "CF Industries":"CF", 106 | "Charles River Laboratories":"CRL", 107 | "Charles Schwab Corporation":"SCHW", 108 | "Charter Communications":"CHTR", 109 | "Chevron Corporation":"CVX", 110 | "Chipotle Mexican Grill":"CMG", 111 | "Chubb Limited":"CB", 112 | "Church & Dwight":"CHD", 113 | "Cigna":"CI", 114 | "Cincinnati Financial":"CINF", 115 | "Cintas":"CTAS", 116 | "Cisco":"CSCO", 117 | "Citigroup":"C", 118 | "Citizens Financial Group":"CFG", 119 | "Citrix":"CTXS", 120 | "Clorox":"CLX", 121 | "CME Group":"CME", 122 | "CMS Energy":"CMS", 123 | "The Coca-Cola Company":"KO", 124 | "Cognizant":"CTSH", 125 | "Colgate-Palmolive":"CL", 126 | "Comcast":"CMCSA", 127 | "Comerica":"CMA", 128 | "Conagra Brands":"CAG", 129 | "ConocoPhillips":"COP", 130 | "Consolidated Edison":"ED", 131 | "Constellation Brands":"STZ", 132 | "Constellation Energy":"CEG", 133 | "CooperCompanies":"COO", 134 | "Copart":"CPRT", 135 | "Corning Inc.":"GLW", 136 | "Corteva":"CTVA", 137 | "Costco":"COST", 138 | "Coterra":"CTRA", 139 | "Crown Castle":"CCI", 140 | "CSX":"CSX", 141 | "Cummins":"CMI", 142 | "CVS Health":"CVS", 143 | "D.R. Horton":"DHI", 144 | "Danaher Corporation":"DHR", 145 | "Darden Restaurants":"DRI", 146 | "DaVita Inc.":"DVA", 147 | "John Deere":"DE", 148 | "Delta Air Lines":"DAL", 149 | "Dentsply Sirona":"XRAY", 150 | "Devon Energy":"DVN", 151 | "Dexcom":"DXCM", 152 | "Diamondback Energy":"FANG", 153 | "Digital Realty":"DLR", 154 | "Discover Financial":"DFS", 155 | "Dish Network":"DISH", 156 | "Disney":"DIS", 157 | "Dollar General":"DG", 158 | "Dollar Tree":"DLTR", 159 | "Dominion Energy":"D", 160 | "Domino's":"DPZ", 161 | "Dover Corporation":"DOV", 162 | "Dow Inc.":"DOW", 163 | "DTE Energy":"DTE", 164 | "Duke Energy":"DUK", 165 | "Duke Realty":"DRE", 166 | "DuPont":"DD", 167 | "DXC Technology":"DXC", 168 | "Eastman Chemical Company":"EMN", 169 | "Eaton Corporation":"ETN", 170 | "eBay":"EBAY", 171 | "Ecolab":"ECL", 172 | "Edison International":"EIX", 173 | "Edwards Lifesciences":"EW", 174 | "Electronic Arts":"EA", 175 | "Eli Lilly and Company":"LLY", 176 | "Emerson Electric":"EMR", 177 | "Enphase":"ENPH", 178 | "Entergy":"ETR", 179 | "EOG Resources":"EOG", 180 | "EPAM Systems":"EPAM", 181 | "Equifax":"EFX", 182 | "Equinix":"EQIX", 183 | "Equity Residential":"EQR", 184 | "Essex Property Trust":"ESS", 185 | "The Estée Lauder Companies":"EL", 186 | "Etsy":"ETSY", 187 | "Everest Re":"RE", 188 | "Evergy":"EVRG", 189 | "Eversource":"ES", 190 | "Exelon":"EXC", 191 | "Expedia Group":"EXPE", 192 | "Expeditors International":"EXPD", 193 | "Extra Space Storage":"EXR", 194 | "ExxonMobil":"XOM", 195 | "F5":"FFIV", 196 | "FactSet":"FDS", 197 | "Fastenal":"FAST", 198 | "Federal Realty":"FRT", 199 | "FedEx":"FDX", 200 | "Fifth Third Bank":"FITB", 201 | "First Republic Bank":"FRC", 202 | "FirstEnergy":"FE", 203 | "FIS":"FIS", 204 | "Fiserv":"FISV", 205 | "Fleetcor":"FLT", 206 | "FMC Corporation":"FMC", 207 | "Ford Motor Company":"F", 208 | "Fortinet":"FTNT", 209 | "Fortive":"FTV", 210 | "Fortune Brands Home & Security":"FBHS", 211 | "Fox Corporation (Class A)":"FOXA", 212 | "Fox Corporation (Class B)":"FOX", 213 | "Franklin Templeton":"BEN", 214 | "Freeport-McMoRan":"FCX", 215 | "Garmin":"GRMN", 216 | "Gartner":"IT", 217 | "Generac":"GNRC", 218 | "General Dynamics":"GD", 219 | "General Electric":"GE", 220 | "General Mills":"GIS", 221 | "General Motors":"GM", 222 | "Genuine Parts Company":"GPC", 223 | "Gilead Sciences":"GILD", 224 | "Globe Life":"GL", 225 | "Global Payments":"GPN", 226 | "Goldman Sachs":"GS", 227 | "Halliburton":"HAL", 228 | "Hartford (The)":"HIG", 229 | "Hasbro":"HAS", 230 | "HCA Healthcare":"HCA", 231 | "Healthpeak":"PEAK", 232 | "Henry Schein":"HSIC", 233 | "Hershey's":"HSY", 234 | "Hess Corporation":"HES", 235 | "Hewlett Packard Enterprise":"HPE", 236 | "Hilton Worldwide":"HLT", 237 | "Hologic":"HOLX", 238 | "The Home Depot":"HD", 239 | "Honeywell":"HON", 240 | "Hormel Foods":"HRL", 241 | "Host Hotels & Resorts":"HST", 242 | "Howmet Aerospace":"HWM", 243 | "HP Inc.":"HPQ", 244 | "Humana":"HUM", 245 | "Huntington Bancshares":"HBAN", 246 | "Huntington Ingalls Industries":"HII", 247 | "IBM":"IBM", 248 | "IDEX Corporation":"IEX", 249 | "Idexx Laboratories":"IDXX", 250 | "Illinois Tool Works":"ITW", 251 | "Illumina":"ILMN", 252 | "Incyte":"INCY", 253 | "Ingersoll Rand":"IR", 254 | "Intel":"INTC", 255 | "Intercontinental Exchange":"ICE", 256 | "International Paper":"IP", 257 | "The Interpublic Group of Companies":"IPG", 258 | "International Flavors & Fragrances":"IFF", 259 | "Intuit":"INTU", 260 | "Intuitive Surgical":"ISRG", 261 | "Invesco":"IVZ", 262 | "IQVIA":"IQV", 263 | "Iron Mountain":"IRM", 264 | "J.B. Hunt":"JBHT", 265 | "Jack Henry & Associates":"JKHY", 266 | "Jacobs Engineering Group":"J", 267 | "Johnson & Johnson":"JNJ", 268 | "Johnson Controls":"JCI", 269 | "JPMorgan Chase":"JPM", 270 | "Juniper Networks":"JNPR", 271 | "Kellogg's":"K", 272 | "Keurig Dr Pepper":"KDP", 273 | "KeyCorp":"KEY", 274 | "Keysight":"KEYS", 275 | "Kimberly-Clark":"KMB", 276 | "Kimco Realty":"KIM", 277 | "Kinder Morgan":"KMI", 278 | "KLA Corporation":"KLAC", 279 | "Kraft Heinz":"KHC", 280 | "Kroger":"KR", 281 | "L3Harris":"LHX", 282 | "LabCorp":"LH", 283 | "Lam Research":"LRCX", 284 | "Lamb Weston":"LW", 285 | "Las Vegas Sands":"LVS", 286 | "Leidos":"LDOS", 287 | "Lennar":"LEN", 288 | "Lincoln Financial":"LNC", 289 | "Linde plc":"LIN", 290 | "Live Nation Entertainment":"LYV", 291 | "LKQ Corporation":"LKQ", 292 | "Lockheed Martin":"LMT", 293 | "Loews Corporation":"L", 294 | "Lowe's":"LOW", 295 | "Lumen Technologies":"LUMN", 296 | "LyondellBasell":"LYB", 297 | "M&T Bank":"MTB", 298 | "Marathon Oil":"MRO", 299 | "Marathon Petroleum":"MPC", 300 | "MarketAxess":"MKTX", 301 | "Marriott International":"MAR", 302 | "Marsh McLennan":"MMC", 303 | "Martin Marietta Materials":"MLM", 304 | "Masco":"MAS", 305 | "Mastercard":"MA", 306 | "Match Group":"MTCH", 307 | "McCormick & Company":"MKC", 308 | "McDonald's":"MCD", 309 | "McKesson":"MCK", 310 | "Medtronic":"MDT", 311 | "Merck & Co.":"MRK", 312 | "Meta Platforms":"META", 313 | "MetLife":"MET", 314 | "Mettler Toledo":"MTD", 315 | "MGM Resorts":"MGM", 316 | "Microchip Technology":"MCHP", 317 | "Micron Technology":"MU", 318 | "Microsoft":"MSFT", 319 | "Mid-America Apartment Communities":"MAA", 320 | "Moderna":"MRNA", 321 | "Mohawk Industries":"MHK", 322 | "Molina Healthcare":"MOH", 323 | "Molson Coors Beverage Company":"TAP", 324 | "Mondelez International":"MDLZ", 325 | "Monolithic Power Systems":"MPWR", 326 | "Monster Beverage":"MNST", 327 | "Moody's Corporation":"MCO", 328 | "Morgan Stanley":"MS", 329 | "The Mosaic Company":"MOS", 330 | "Motorola Solutions":"MSI", 331 | "MSCI":"MSCI", 332 | "Nasdaq":"NDAQ", 333 | "NetApp":"NTAP", 334 | "Netflix":"NFLX", 335 | "Newell Brands":"NWL", 336 | "Newmont":"NEM", 337 | "News Corp (Class A)":"NWSA", 338 | "News Corp (Class B)":"NWS", 339 | "NextEra Energy":"NEE", 340 | "Nielsen Holdings":"NLSN", 341 | "Nike":"NKE", 342 | "NiSource":"NI", 343 | "Nordson Corporation":"NDSN", 344 | "Norfolk Southern Railway":"NSC", 345 | "Northern Trust":"NTRS", 346 | "Northrop Grumman":"NOC", 347 | "NortonLifeLock":"NLOK", 348 | "Norwegian Cruise Line Holdings":"NCLH", 349 | "NRG Energy":"NRG", 350 | "Nucor":"NUE", 351 | "Nvidia":"NVDA", 352 | "NVR":"NVR", 353 | "NXP Semiconductors":"NXPI", 354 | "O'Reilly Auto Parts":"ORLY", 355 | "Occidental Petroleum":"OXY", 356 | "Old Dominion":"ODFL", 357 | "Omnicom Group":"OMC", 358 | "ON Semiconductor":"ON", 359 | "Oneok":"OKE", 360 | "Oracle Corporation":"ORCL", 361 | "Organon & Co.":"OGN", 362 | "Otis Worldwide":"OTIS", 363 | "Paccar":"PCAR", 364 | "Packaging Corporation of America":"PKG", 365 | "Paramount Global":"PARA", 366 | "Parker Hannifin":"PH", 367 | "Paychex":"PAYX", 368 | "Paycom":"PAYC", 369 | "PayPal":"PYPL", 370 | "Penn National Gaming":"PENN", 371 | "Pentair":"PNR", 372 | "PepsiCo":"PEP", 373 | "PerkinElmer":"PKI", 374 | "Pfizer":"PFE", 375 | "Philip Morris International":"PM", 376 | "Phillips 66":"PSX", 377 | "Pinnacle West":"PNW", 378 | "Pioneer Natural Resources":"PXD", 379 | "PNC Financial Services":"PNC", 380 | "Pool Corporation":"POOL", 381 | "PPG Industries":"PPG", 382 | "PPL Corporation":"PPL", 383 | "Principal Financial Group":"PFG", 384 | "Procter & Gamble":"PG", 385 | "Progressive Corporation":"PGR", 386 | "Prologis":"PLD", 387 | "Prudential Financial":"PRU", 388 | "Public Service Enterprise Group":"PEG", 389 | "PTC":"PTC", 390 | "Public Storage":"PSA", 391 | "PulteGroup":"PHM", 392 | "PVH":"PVH", 393 | "Qorvo":"QRVO", 394 | "Quanta Services":"PWR", 395 | "Qualcomm":"QCOM", 396 | "Quest Diagnostics":"DGX", 397 | "Ralph Lauren Corporation":"RL", 398 | "Raymond James":"RJF", 399 | "Raytheon Technologies":"RTX", 400 | "Realty Income":"O", 401 | "Regency Centers":"REG", 402 | "Regeneron":"REGN", 403 | "Regions Financial Corporation":"RF", 404 | "Republic Services":"RSG", 405 | "ResMed":"RMD", 406 | "Robert Half":"RHI", 407 | "Rockwell Automation":"ROK", 408 | "Rollins":"ROL", 409 | "Roper Technologies":"ROP", 410 | "Ross Stores":"ROST", 411 | "Royal Caribbean Group":"RCL", 412 | "S&P Global":"SPGI", 413 | "Salesforce":"CRM", 414 | "SBA Communications":"SBAC", 415 | "Schlumberger":"SLB", 416 | "Seagate Technology":"STX", 417 | "Sealed Air":"SEE", 418 | "Sempra Energy":"SRE", 419 | "ServiceNow":"NOW", 420 | "Sherwin-Williams":"SHW", 421 | "Signature Bank":"SBNY", 422 | "Simon Property Group":"SPG", 423 | "Skyworks Solutions":"SWKS", 424 | "The J.M. Smucker Company":"SJM", 425 | "Snap-on":"SNA", 426 | "SolarEdge":"SEDG", 427 | "Southern Company":"SO", 428 | "Southwest Airlines":"LUV", 429 | "Stanley Black & Decker":"SWK", 430 | "Starbucks":"SBUX", 431 | "State Street Corporation":"STT", 432 | "Steris":"STE", 433 | "Stryker Corporation":"SYK", 434 | "SVB Financial":"SIVB", 435 | "Synchrony Financial":"SYF", 436 | "Synopsys":"SNPS", 437 | "Sysco":"SYY", 438 | "T-Mobile US":"TMUS", 439 | "T. Rowe Price":"TROW", 440 | "Take-Two Interactive":"TTWO", 441 | "Tapestry":"TPR", 442 | "Target Corporation":"TGT", 443 | "TE Connectivity":"TEL", 444 | "Teledyne Technologies":"TDY", 445 | "Teleflex":"TFX", 446 | "Teradyne":"TER", 447 | "Tesla":"TSLA", 448 | "Texas Instruments":"TXN", 449 | "Textron":"TXT", 450 | "Thermo Fisher Scientific":"TMO", 451 | "TJX Companies":"TJX", 452 | "Tractor Supply":"TSCO", 453 | "Trane Technologies":"TT", 454 | "TransDigm Group":"TDG", 455 | "The Travelers Companies":"TRV", 456 | "Trimble Inc.":"TRMB", 457 | "Truist":"TFC", 458 | "Twitter Inc.":"TWTR", 459 | "Tyler Technologies":"TYL", 460 | "Tyson Foods":"TSN", 461 | "U.S. Bank":"USB", 462 | "UDR":"UDR", 463 | "Ulta Beauty":"ULTA", 464 | "Union Pacific Corporation":"UNP", 465 | "United Airlines Holdings":"UAL", 466 | "United Parcel Service":"UPS", 467 | "United Rentals":"URI", 468 | "UnitedHealth Group":"UNH", 469 | "Universal Health Services":"UHS", 470 | "Valero Energy":"VLO", 471 | "Ventas":"VTR", 472 | "Verisign":"VRSN", 473 | "Verisk":"VRSK", 474 | "Verizon":"VZ", 475 | "Vertex Pharmaceuticals":"VRTX", 476 | "VF Corporation":"VFC", 477 | "Viatris":"VTRS", 478 | "Vici Properties":"VICI", 479 | "Visa Inc.":"V", 480 | "Vornado Realty Trust":"VNO", 481 | "Vulcan Materials Company":"VMC", 482 | "Wabtec":"WAB", 483 | "Walgreens Boots Alliance":"WBA", 484 | "Walmart":"WMT", 485 | "Warner Bros. Discovery":"WBD", 486 | "Waste Management":"WM", 487 | "Waters Corporation":"WAT", 488 | "WEC Energy Group":"WEC", 489 | "Wells Fargo":"WFC", 490 | "Welltower":"WELL", 491 | "West Pharmaceutical Services":"WST", 492 | "Western Digital":"WDC", 493 | "WestRock":"WRK", 494 | "Weyerhaeuser":"WY", 495 | "Whirlpool Corporation":"WHR", 496 | "Williams Companies":"WMB", 497 | "Willis Towers Watson":"WTW", 498 | "W. W. Grainger":"GWW", 499 | "Wynn Resorts":"WYNN", 500 | "Xcel Energy":"XEL", 501 | "Xylem Inc.":"XYL", 502 | "Yum! Brands":"YUM", 503 | "Zebra Technologies":"ZBRA", 504 | "Zimmer Biomet":"ZBH", 505 | "Zions Bancorporation":"ZION", 506 | "Zoetis":"ZTS" 507 | } -------------------------------------------------------------------------------- /backend/nlp.py: -------------------------------------------------------------------------------- 1 | import re 2 | import json 3 | import spacy 4 | import gensim 5 | import pandas as pd 6 | 7 | from gensim import corpora, models 8 | from cleanco import basename 9 | from nltk.stem import WordNetLemmatizer 10 | from nltk.sentiment.vader import SentimentIntensityAnalyzer 11 | 12 | NER = spacy.load("en_core_web_sm") 13 | 14 | def extract_hashtags(tweet) -> str: 15 | '''Extract hashtags from a tweet''' 16 | hashtag_re = re.compile("(?:^|\s)[##]{1}(\w+)", re.UNICODE) 17 | return hashtag_re.findall(tweet) 18 | 19 | def extract_mentions(tweet) -> str: 20 | '''Extract all non link mentions from a tweet''' 21 | tweet = remove_links(tweet) 22 | mention_re = re.compile("(?:^|\s)[@ @]{1}([^\s#<>[\]|{}]+)", re.UNICODE) 23 | return mention_re.findall(tweet) 24 | 25 | def remove_links(tweet) -> str: 26 | '''Removes links from a tweet''' 27 | tweet = re.sub(r'http\S+', '', tweet) # remove http links 28 | tweet = re.sub(r'bit.ly/\S+', '', tweet) # remove bitly links 29 | return tweet 30 | 31 | def preprocess_tweet(tweet) -> str: 32 | '''Removes links, punctuation, numbers and extra spacing from a tweet''' 33 | tweet = remove_links(tweet) 34 | tweet = tweet.lower() # lower case 35 | tweet = re.sub('[!"$%&\'()*+,-./:;<=>?[\\]^_`{|}~•@]+', '', tweet) # strip punctuation 36 | tweet = re.sub('\s+', ' ', tweet) # remove double spacing 37 | tweet = re.sub('([0-9]+)', '', tweet) # remove numbers 38 | return tweet 39 | 40 | def lemmatize(token) -> list: 41 | '''Returns lemmatization of a token''' 42 | return WordNetLemmatizer().lemmatize(token, pos='v') 43 | 44 | def tokenize(tweet) -> str: 45 | '''Returns tokenized representation of words in lemma form excluding stopwords''' 46 | result = [] 47 | for token in gensim.utils.simple_preprocess(tweet): 48 | if token not in gensim.parsing.preprocessing.STOPWORDS: 49 | result.append(lemmatize(token)) 50 | return ' '.join(result) 51 | 52 | def vader_sentiment(text): 53 | """Return vader scores (neg, pos, neu and compound) of a text""" 54 | sid = SentimentIntensityAnalyzer() 55 | return sid.polarity_scores(text) 56 | 57 | def named_entity_recognition(text): 58 | """Extract named entities from a text""" 59 | # Find a way to speed this up 60 | entities = NER(text) 61 | return [entity.text for entity in entities.ents if entity.label_ == "ORG" ] 62 | -------------------------------------------------------------------------------- /backend/service.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import json 5 | import threading 6 | import time 7 | import requests 8 | import twint 9 | import pandas as pd 10 | import yfinance as yf 11 | 12 | from nlp import * 13 | from constants import USERNAMES, COMPANIES, DIRECTORIES 14 | 15 | class Service: 16 | LIMIT = 100 17 | 18 | def __init__(self, usernames): 19 | self.users = usernames 20 | # TODO: multithreading safe queue 21 | self.queue = [] 22 | self._run() 23 | 24 | def _get_user_info(self, username) -> tuple: 25 | '''Retrieve a user's base Twitter information 26 | Args: 27 | username - string 28 | Return: 29 | object - Twint User object 30 | ''' 31 | c = twint.Config() 32 | c.Username = username 33 | c.Hide_output = True 34 | c.Store_object = True 35 | twint.run.Lookup(c) 36 | return twint.output, twint.output.users_list[0] 37 | 38 | def _tweets_by_username(self, username, limit=1, to_csv=False) -> list: 39 | '''Retrieve a user's tweets. Limit's increment is 100, so 1 is the latest 100 tweets. 40 | Args: 41 | username - string 42 | limit - tweet limit (default 1) 43 | Returns: 44 | list - array of twint.tweet objects 45 | ''' 46 | c = twint.Config() 47 | c.Username = username 48 | c.Limit = limit 49 | c.Store_object = True 50 | c.Hide_output = True 51 | 52 | if to_csv: 53 | c.Store_csv = True 54 | c.Output = f'./tweets/{username}.csv' 55 | 56 | twint.run.Search(c) 57 | return twint.output.tweets_list 58 | 59 | def _get_historical_data(self, ticker) -> dict: 60 | '''Retrieve a ticker's historical data. 61 | Args: 62 | ticker - 'APPL' 63 | Returns: 64 | dict - date, low, high, open, close, volume, dividends, stock splits 65 | ''' 66 | historical_data = yf.Ticker(ticker).history(period="max").reset_index().dropna() 67 | historical_data['Date'] = historical_data['Date'].dt.strftime('%Y-%m-%d') 68 | return { k.lower(): v for k, v in historical_data.to_dict('list').items() } 69 | 70 | def _preprocess_tweet(self, tweet) -> dict: 71 | '''Clean, tokenize and extract mentions/hashtags from the tweet. Calculate the 72 | Vader Sentiment and determine if any companies exist through Named Entity Recognition. 73 | Args: 74 | tweet - Twint tweet object 75 | ''' 76 | vs = vader_sentiment(tweet.tweet) 77 | clean = preprocess_tweet(tweet.tweet) 78 | tweet = { 79 | 'date': tweet.datestamp, 80 | 'time': tweet.timestamp, 81 | 'username': tweet.username, 82 | 'tweet': tweet.tweet, 83 | 'companies': named_entity_recognition(tweet.tweet), 84 | 'pos': vs['pos'], 85 | 'neg': vs['neg'], 86 | 'neu': vs['neu'], 87 | 'compound': vs['compound'], 88 | 'mentions': extract_mentions(tweet.tweet), 89 | 'hashtags': extract_hashtags(tweet.tweet), 90 | 'clean': clean, 91 | 'tokens': tokenize(clean), 92 | } 93 | self.queue.append(tweet) 94 | return tweet 95 | 96 | def _fetch_new_tweets(self) -> None: 97 | '''Update and process latest user tweets''' 98 | for user in self.users: 99 | latest_tweet = tweets_by_username(user)[0] 100 | 101 | with open(f'./tweets/{user}.json', 'r+') as f: 102 | tweets = json.load(f) 103 | if latest_tweet.tweet != tweets[0]['tweet']: 104 | tweet = self._preprocess_tweet(latest_tweet) 105 | tweets.insert(0, tweet) 106 | 107 | f.seek(0) 108 | json.dump({'tweets': tweets}, f, indent=4) 109 | f.truncate() 110 | 111 | def _company_exists(self, company) -> bool: 112 | '''Checks if a company exists within our COMPANIES dict''' 113 | # TODO: better way of checking 114 | if company in COMPANIES or company in COMPANIES.values(): 115 | return True 116 | return False 117 | 118 | def _queue_manager(self) -> None: 119 | '''Manages the company queue, determines if a company exists''' 120 | while True: 121 | if len(self.queue) > 0: 122 | queue_item = self.queue.pop(0) 123 | for company in queue_item['companies']: 124 | if self._company_exists(company): 125 | company_name, hist = None, None 126 | try: 127 | hist = self._get_historical_data(COMPANIES[company]) 128 | company_name = company 129 | except KeyError: 130 | # account for ticker values 131 | hist = self._get_historical_data(company) 132 | company_name = list(COMPANIES.keys())[list(COMPANIES.values()).index(company)] 133 | 134 | with open(f'./historical/{company_name}.json', 'w+') as f: 135 | f.seek(0) 136 | json.dump(hist, f, indent=4) 137 | f.truncate() 138 | 139 | with open(f'./mentions/{queue_item["username"]}.json', 'r+') as f: 140 | mentions = json.load(f) 141 | if company_name not in mentions: 142 | mentions[company_name] = [] 143 | mentions[company_name].append(queue_item) 144 | 145 | f.seek(0) 146 | json.dump(mentions, f, indent=4) 147 | f.truncate() 148 | 149 | def _company_manager(self) -> None: 150 | '''Fetches & updates historical share price data of a given ticker''' 151 | for fn in os.listdir(f'{os.curdir}/historical/'): 152 | hist = self._get_historical_data(fn.split(".")[0]) 153 | with open(f'./historical/{fn}', 'r+') as f: 154 | f.seek(0) 155 | json.dump(hist, f, indent=4) 156 | f.truncate() 157 | 158 | def _prerequisites(self) -> None: 159 | '''Ensure the required files exist. Create files & fetch base information if they do not.''' 160 | user_files = os.listdir(f'{os.curdir}/users') 161 | for i, user in enumerate(self.users): 162 | if f'{user}.json' not in user_files: 163 | tw, u = self._get_user_info(user) 164 | tw.clean_lists() 165 | 166 | user_info = { 167 | 'name': u.name, 168 | 'username': u.username, 169 | 'bio': u.bio, 170 | 'avatar': ''.join(u.avatar.split('_normal')), 171 | 'followers': u.followers, 172 | 'verified': u.is_verified, 173 | } 174 | 175 | with open(f'./users/{user}.json', 'w') as f: 176 | json.dump(user_info, f, indent=4) 177 | with open(f'./tweets/{user}.json', 'w') as f: 178 | json.dump([self._preprocess_tweet(tweet) for tweet in self._tweets_by_username(user, limit=self.LIMIT)], f, indent=4) 179 | with open(f'./mentions/{user}.json', 'w') as f: 180 | json.dump({}, f, indent=4) 181 | 182 | def _run(self) -> None: 183 | self._prerequisites() 184 | self._queue_manager() 185 | self._company_manager() 186 | # self._fetch_new_tweets() 187 | 188 | if __name__ == "__main__": 189 | if len(sys.argv) > 1: 190 | Service(USERNAMES) 191 | else: 192 | for directory in DIRECTORIES: 193 | for f in os.listdir(directory): 194 | os.remove(os.path.join(directory, f)) 195 | 196 | -------------------------------------------------------------------------------- /webapp/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /webapp/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.4", 7 | "@testing-library/react": "^13.3.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "react-icons": "^4.4.0", 12 | "react-scripts": "5.0.1", 13 | "recharts": "^2.1.12", 14 | "web-vitals": "^2.1.4" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /webapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semaaJ/TwitterStockMonitor/cf0709bba93c7c727b6917848ac8efa2ded9d7c7/webapp/public/favicon.ico -------------------------------------------------------------------------------- /webapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 |{ tweet.date } { tweet.time }
18 |{ tweet.tweet }
21 |{ user.bio }
21 |"{ mention.tweet }"
76 |{ mention.neg.toFixed(3) }
77 |{ mention.neu.toFixed(3) }
78 |{ mention.pos.toFixed(3) }
79 |{ mention.compound.toFixed(3) }
80 |