├── .gitignore ├── .gitattributes ├── requirements.txt ├── requirements2.txt ├── rateLimitCheck.py ├── LICENSE ├── website_generator.py ├── README.md ├── stocklist.py ├── grapher.py ├── templates └── template.html ├── market_scanner.py ├── dynamic.html └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | __pycache__/ 3 | __pycache__ 4 | .vscode 5 | build/ 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Using a venv with python@3.10.15 2 | 3 | mplcursors==0.3 4 | numpy==2.0.0 5 | yfinance==0.2.44 6 | tqdm==4.48.0 7 | joblib==0.16.0 8 | matplotlib==3.9.2 9 | Flask==2.2.2 10 | Flask_FlatPages==0.7.2 11 | pandas==2.2.3 12 | Frozen_Flask==1.0.2 13 | python_dateutil==2.9.0 14 | Werkzeug==2.2.2 15 | quandl==3.7.0 16 | joblib==1.4.2 -------------------------------------------------------------------------------- /requirements2.txt: -------------------------------------------------------------------------------- 1 | # Using a venv with python@3.10.15 2 | 3 | beautifulsoup4==4.12.3 4 | certifi==2024.8.30 5 | charset-normalizer==3.4.0 6 | click==8.1.7 7 | contourpy==1.3.0 8 | cycler==0.12.1 9 | Flask==2.2.2 10 | Flask-FlatPages==0.7.2 11 | fonttools==4.54.1 12 | Frozen-Flask==1.0.2 13 | frozendict==2.4.5 14 | html5lib==1.1 15 | idna==3.10 16 | inflection==0.5.1 17 | itsdangerous==2.2.0 18 | Jinja2==3.1.4 19 | joblib==1.4.2 20 | kiwisolver==1.4.7 21 | lxml==5.3.0 22 | Markdown==3.7 23 | MarkupSafe==3.0.1 24 | matplotlib==3.9.2 25 | more-itertools==10.5.0 26 | mplcursors==0.3 27 | multitasking==0.0.11 28 | numpy==2.0.0 29 | packaging==24.1 30 | pandas==2.2.3 31 | peewee==3.17.6 32 | pillow==10.4.0 33 | platformdirs==4.3.6 34 | pyparsing==3.1.4 35 | python-dateutil==2.9.0.post0 36 | pytz==2024.2 37 | PyYAML==6.0.2 38 | Quandl==3.7.0 39 | requests==2.32.3 40 | six==1.16.0 41 | soupsieve==2.6 42 | tqdm==4.48.0 43 | tzdata==2024.2 44 | urllib3==2.2.3 45 | webencodings==0.5.1 46 | Werkzeug==2.2.2 47 | yfinance==0.2.44 48 | -------------------------------------------------------------------------------- /rateLimitCheck.py: -------------------------------------------------------------------------------- 1 | import yfinance as yf 2 | from datetime import date 3 | import dateutil.relativedelta 4 | import datetime 5 | import pandas as pd 6 | from pandas_datareader import data as pdr 7 | import quandl 8 | 9 | 10 | def getData(ticker): 11 | currentDate = datetime.date.today() + datetime.timedelta(days=1) 12 | pastDate = currentDate - \ 13 | dateutil.relativedelta.relativedelta(months=3) 14 | data = yf.download(ticker, pastDate, currentDate) 15 | #with pd.option_context('display.max_rows', None, 'display.max_columns', None): 16 | print(data[["Volume"]]) 17 | 18 | 19 | getData("MSFT") 20 | 21 | #when on probation, 18 calls results in a lock 22 | 23 | def getQuan(): 24 | start = currentDate = datetime.date.today() + datetime.timedelta(days=1) 25 | end = pastDate = currentDate - \ 26 | dateutil.relativedelta.relativedelta(months=1) 27 | 28 | mydata = quandl.get("WIKI/AAPL", start_date=pastDate, end_date=currentDate, rows=50) 29 | mydata = mydata["Volume"] 30 | print(mydata) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sam Pomerantz 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. 22 | -------------------------------------------------------------------------------- /website_generator.py: -------------------------------------------------------------------------------- 1 | import flask 2 | from flask_flatpages import FlatPages 3 | from flask_frozen import Freezer 4 | from flask import Flask, request, send_from_directory, render_template 5 | from shutil import copyfile 6 | import os 7 | import shutil 8 | import numpy 9 | from market_scanner import mainObj 10 | 11 | # this is used by me AUTOMATICALLY to update the web page you can find at: https://sampom100.github.io/UnusualVolumeDetector/ 12 | 13 | app = flask.Flask(__name__, static_url_path='') 14 | app.config["DEBUG"] = False 15 | app.config['SECRET_KEY'] = 'deditaded wam' 16 | pages = FlatPages(app) 17 | freezer = Freezer(app) 18 | 19 | 20 | @app.after_request 21 | def after_request(response): 22 | response.headers.add('Access-Control-Allow-Origin', 'localhost*,192.168.*') 23 | response.headers.add('Access-Control-Allow-Headers', 24 | 'Content-Type,Authorization') 25 | response.headers.add('Access-Control-Allow-Methods', 26 | 'GET,PUT,POST,DELETE,OPTIONS') 27 | return response 28 | 29 | 30 | @app.route('/', methods=['GET']) 31 | def home(): 32 | return render_template('template.html', stocks=stock) 33 | 34 | 35 | def sort_by_volume(data): 36 | def get_volume(item): 37 | return int(item['TargetVolume'].replace(',', '')) 38 | return sorted(data, key=get_volume, reverse=True) 39 | 40 | if __name__ == "__main__": 41 | os.system('git fetch') 42 | stock = sort_by_volume(mainObj().main_func()[:15]) 43 | freezer.freeze() 44 | copyfile('build/index.html', 'index.html') 45 | # shutil.rmtree('build/') 46 | # I'm lazy :) 47 | # os.system('git add .') 48 | # os.system('git commit -m "updated website"') 49 | # os.system('git push origin master') 50 | # app.run(host='0.0.0.0', port='5000') # run the app on LAN 51 | # app.run() # run the app on your machine 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unusual Volume Detector --- [New Website! ](https://unusualvolume.info/): 2 | 3 | This scans every ticker on the market, gets their last 5 months of volume history, and alerts you when a stock's volume exceeds 10 standard deviations from the mean within the last 3 days. (these numbers are all adjustable). Helps find anomalies in the stock market 4 | 5 | ## Easiest way to see this: 6 | 7 | Go to the [website](https://unusualvolume.info/) 8 | 9 | 10 | ### How to run the script: 11 | - Download your favorite Python IDE. (I use VSCode) 12 | - Get my script from GitHub 13 | - Open the script in your IDE and install all required dependancies by typing pip install -r requirements.txt into the IDE's terminal. You can get to the the terminal on VSC by pressing CMD and ` at the same time. 14 | - Run the market_scanner.py and it will print out results into the terminal 15 | - You can also graph any ticker's volume in grapher.py 16 | 17 | ### Controlling the Script 18 | - Line 21 controls the amount of months of historical volume the script gets 19 | - Line 22 controls the amount of days before today that it will alert you 20 | - Line 23 controls the number of standard deviations away from the mean volume 21 | - Line 116, "n-jobs" controls the number of threads the script runs on, which I lowered to avoid new rate limits 22 | 23 | 24 | 25 | Screen Shot 2020-08-01 at 3 24 03 AM 26 | 27 | ![j67nuj3cl0e51](https://user-images.githubusercontent.com/28206070/88943805-8d1ea080-d251-11ea-81ed-04138e21bf1f.png) 28 | 29 | ![ue395lbgl0e51](https://user-images.githubusercontent.com/28206070/88943804-8d1ea080-d251-11ea-8c03-3f42da8849f6.png) 30 | 31 | ![s9jtygygl0e51](https://user-images.githubusercontent.com/28206070/88943801-8c860a00-d251-11ea-833b-8e7685360ab2.png) 32 | 33 | 34 | ### Websites 35 | 36 | [Main Website](https://sampom100.github.io/UnusualVolumeDetector/) 37 | 38 | [alternate website](http://165.22.228.6/) 39 | 40 | [alternate credit](https://www.removeddit.com/r/wallstreetbets/comments/i10mif/i_made_a_website_for_that_scanner_made_by_that/) 41 | 42 | 43 | ### Donations 44 | 45 | If you enjoy my work, please [donate here](https://www.paypal.me/SamPom100) 46 | -------------------------------------------------------------------------------- /stocklist.py: -------------------------------------------------------------------------------- 1 | from ftplib import FTP 2 | import os 3 | import errno 4 | 5 | 6 | # this is used to get all tickers from the market. 7 | 8 | 9 | exportList = [] 10 | 11 | 12 | class NasdaqController: 13 | def getList(self): 14 | return exportList 15 | 16 | def __init__(self, update=False): 17 | 18 | #"otherlisted": "data/otherlisted.txt", 19 | self.filenames = { 20 | "nasdaqlisted": "data/nasdaqlisted.txt" 21 | } 22 | 23 | # Update lists only if update = True 24 | 25 | if update == True: 26 | self.ftp = FTP("ftp.nasdaqtrader.com") 27 | self.ftp.login() 28 | 29 | #print("Nasdaq Controller: Welcome message: " + self.ftp.getwelcome()) 30 | 31 | self.ftp.cwd("SymbolDirectory") 32 | 33 | for filename, filepath in self.filenames.items(): 34 | if not os.path.exists(os.path.dirname(filepath)): 35 | try: 36 | os.makedirs(os.path.dirname(filepath)) 37 | except OSError as exc: # Guard against race condition 38 | if exc.errno != errno.EEXIST: 39 | raise 40 | 41 | self.ftp.retrbinary("RETR " + filename + 42 | ".txt", open(filepath, 'wb').write) 43 | 44 | all_listed = open("data/alllisted.txt", 'w') 45 | 46 | for filename, filepath in self.filenames.items(): 47 | with open(filepath, "r") as file_reader: 48 | for i, line in enumerate(file_reader, 0): 49 | if i == 0: 50 | continue 51 | 52 | line = line.strip().split("|") 53 | 54 | # line[6] and line[4] is for ETFs. Let's skip those to make this faster. 55 | if line[0] == "" or line[1] == "" or (filename == 'nasdaqlisted' and line[6] == 'Y') or (filename == 'otherlisted' and line[4] == 'Y'): 56 | continue 57 | 58 | all_listed.write(line[0] + ",") 59 | global exportList 60 | exportList.append(line[0]) 61 | all_listed.write(line[0] + "|" + line[1] + "\n") 62 | 63 | if __name__ == "__main__": 64 | StocksController = NasdaqController(True) 65 | print(StocksController.getList()) 66 | print("Refresh Done.") -------------------------------------------------------------------------------- /grapher.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import yfinance as yf 3 | from datetime import * 4 | import dateutil.relativedelta 5 | import datetime 6 | import pandas as pd 7 | import mplcursors 8 | import matplotlib 9 | import matplotlib.dates as mdates 10 | from dateutil import parser 11 | import numpy as np 12 | 13 | 14 | # Use this to graph the volume history of your favorite ticker# 15 | 16 | 17 | class mainObj: 18 | def getData(self, ticker): 19 | currentDate = datetime.datetime.strptime( 20 | date.today().strftime("%Y-%m-%d"), "%Y-%m-%d") 21 | pastDate = currentDate - dateutil.relativedelta.relativedelta(months=4) 22 | data = yf.download(ticker, pastDate, currentDate) 23 | return data[["Volume"]] 24 | 25 | def printData(self, data): 26 | with pd.option_context('display.max_rows', None, 'display.max_columns', None): 27 | cleanData_print = data.copy() 28 | cleanData_print.reset_index(level=0, inplace=True) 29 | print(cleanData_print.to_string(index=False)) 30 | 31 | def barGraph(self, data): 32 | data.reset_index(level=0, inplace=True) 33 | tempList = [] 34 | for x in data['Date']: 35 | tempList.append(x.date()) 36 | data['goodDate'] = tempList 37 | data = data.drop('Date', 1) 38 | data.set_index('goodDate', inplace=True) 39 | ################ 40 | fig, ax = plt.subplots(figsize=(15, 7)) 41 | data.plot(kind='bar', ax=ax) 42 | ax.get_yaxis().set_major_formatter( 43 | matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ','))) 44 | mplcursors.cursor(hover=True) 45 | ################ 46 | plt.show() 47 | 48 | def lineGraph(self, data): 49 | data.reset_index(level=0, inplace=True) 50 | fig, ax = plt.subplots(figsize=(15, 7)) 51 | ax.plot(data['Date'], data['Volume']) 52 | ax.get_yaxis().set_major_formatter( 53 | matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ','))) 54 | mplcursors.cursor(hover=True) 55 | currentDate = datetime.datetime.strptime( 56 | date.today().strftime("%Y-%m-%d"), "%Y-%m-%d") 57 | pastDate = currentDate - dateutil.relativedelta.relativedelta(months=4) 58 | plt.show() 59 | 60 | def find_anomalies(self, random_data): 61 | anomalies = [] 62 | random_data_std = np.std(random_data) 63 | random_data_mean = np.mean(random_data) 64 | # Set upper and lower limit to 3 standard deviation 65 | anomaly_cut_off = random_data_std * 4 66 | lower_limit = random_data_mean - anomaly_cut_off 67 | upper_limit = random_data_mean + anomaly_cut_off 68 | # Generate outliers 69 | for outlier in random_data: 70 | if outlier > upper_limit or outlier < lower_limit: 71 | anomalies.append(outlier) 72 | return anomalies 73 | 74 | 75 | main = mainObj() 76 | 77 | # INSTRUCTIONS # 78 | 79 | # change KODK to your desired ticker 80 | # feel free to uncomment 'printData' or 'barGraph' depending on what you'd like 81 | data = main.getData("KODK") 82 | # main.printData(data) 83 | # main.barGraph(data) 84 | main.lineGraph(data) 85 | -------------------------------------------------------------------------------- /templates/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Unusual Volume Scanner 7 | 8 | 9 | 15 | 20 | 25 | 26 | 27 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
50 |

Unusual Volume Scanner

51 |

52 | Get alerted when a stock's volume exceeds 10 standard deviations from the mean 53 | within the last 3 days. Dynamic Version. 54 |

55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | {% for stock in stocks %} 65 | 66 | 72 | 73 | 74 | 75 | {% endfor %} 76 | 77 | 78 | 79 | 80 | 81 |
TickerDateVolume
67 | {{stock['Ticker']}} 71 | {{stock['TargetDate']}}{{stock['TargetVolume']}}
82 | 83 | 84 | 85 | Made by Sam Pomerantz and contributors. 86 | 87 | 88 | 89 | 90 | GitHub 91 | 92 | 93 | • 94 | 95 | 96 | LinkedIn 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | Thank you for visiting 105 | 106 | 107 |
108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /market_scanner.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import yfinance as yf 4 | from datetime import date 5 | import datetime 6 | import numpy as np 7 | import sys 8 | from stocklist import NasdaqController 9 | from tqdm import tqdm 10 | from joblib import Parallel, delayed, parallel_backend 11 | import multiprocessing 12 | import pandas as pd 13 | import quandl 14 | from dateutil.parser import parse 15 | from yfinance.exceptions import YFInvalidPeriodError 16 | import random 17 | 18 | ########################### 19 | # THIS IS THE MAIN SCRIPT # 20 | ########################### 21 | 22 | # Change variables to your liking then run the script 23 | MONTH_CUTTOFF = 6 # 6 24 | DAY_CUTTOFF = 4 # 3 25 | STD_CUTTOFF = 7 # 9 26 | MIN_STOCK_VOLUME = 10000 27 | MIN_PRICE = 20 28 | 29 | 30 | class mainObj: 31 | 32 | def __init__(self): 33 | pass 34 | 35 | def getDataQuandl(self, ticker, pastDate, currentDate): 36 | ticker = "WIKI/"+ticker 37 | mydata = quandl.get(ticker, start_date=pastDate, 38 | end_date=currentDate, rows=50) 39 | mydata = mydata["Volume"] 40 | return mydata 41 | 42 | def getData(self, ticker): 43 | try: 44 | global MONTH_CUTOFF 45 | 46 | sys.stdout = open(os.devnull, "w") 47 | # maybe swap yahoo finance to quandl due to rate limits 48 | try: 49 | data = yf.Ticker(ticker).history(period=str(MONTH_CUTTOFF) + "mo", raise_errors=True) 50 | except (YFInvalidPeriodError) as e: 51 | try: 52 | data = yf.Ticker(ticker).history(period=e.valid_ranges[-1]) 53 | except: 54 | return pd.DataFrame(columns=["Volume"]) 55 | sys.stdout = sys.__stdout__ 56 | 57 | if data.tail(1)["Close"].values[0] < MIN_PRICE: 58 | return pd.DataFrame(columns=["Volume"]) 59 | 60 | # avoid yahoo finance rate limits 61 | time.sleep(random.uniform(0.2, 1.5)) 62 | return data[["Volume"]] 63 | except: 64 | return pd.DataFrame(columns=["Volume"]) 65 | 66 | def find_anomalies(self, data): 67 | global STD_CUTTOFF 68 | global MIN_STOCK_VOLUME 69 | indexs = [] 70 | outliers = [] 71 | data_std = np.std(data['Volume']) 72 | data_mean = np.mean(data['Volume']) 73 | anomaly_cut_off = data_std * STD_CUTTOFF 74 | upper_limit = data_mean + anomaly_cut_off 75 | data.reset_index(level=0, inplace=True) 76 | for i in range(len(data)): 77 | temp = data['Volume'].iloc[i] 78 | if temp > upper_limit and temp > MIN_STOCK_VOLUME: 79 | indexs.append(str(data['Date'].iloc[i])[:-15]) 80 | outliers.append(temp) 81 | d = {'Dates': indexs, 'Volume': outliers} 82 | return d 83 | 84 | def customPrint(self, d, tick): 85 | print("\n\n\n******* " + tick.upper() + " *******") 86 | print("Ticker is: "+tick.upper()) 87 | for i in range(len(d['Dates'])): 88 | str1 = str(d['Dates'][i]) 89 | str2 = str(d['Volume'][i]) 90 | print(str1 + " - " + str2) 91 | print("*********************\n\n\n") 92 | 93 | def days_between(self, d1, d2): 94 | return abs((parse(d2) - parse(d1)).days) 95 | 96 | def parallel_wrapper(self, x, currentDate, positive_scans): 97 | global DAY_CUTTOFF 98 | d = (self.find_anomalies(self.getData(x))) 99 | if d['Dates']: 100 | for i in range(len(d['Dates'])): 101 | if self.days_between(str(currentDate), str(d['Dates'][i])) <= DAY_CUTTOFF: 102 | self.customPrint(d, x) 103 | stock = dict() 104 | stock['Ticker'] = x 105 | stock['TargetDate'] = d['Dates'][0] 106 | stock['TargetVolume'] = str( 107 | '{:,.2f}'.format(d['Volume'][0]))[:-3] 108 | positive_scans.append(stock) 109 | 110 | def main_func(self): 111 | StocksController = NasdaqController(False) 112 | list_of_tickers = StocksController.getList() 113 | currentDate = datetime.date.today().strftime("%m-%d-%Y") 114 | start_time = time.time() 115 | 116 | # positive_scans = [] 117 | # for x in tqdm(list_of_tickers): 118 | # self.parallel_wrapper(x, currentDate, positive_scans) 119 | 120 | manager = multiprocessing.Manager() 121 | positive_scans = manager.list() 122 | 123 | cpu_count = multiprocessing.cpu_count() 124 | try: 125 | with parallel_backend('loky', n_jobs=cpu_count): 126 | Parallel()(delayed(self.parallel_wrapper)(x, currentDate, positive_scans) 127 | for x in tqdm(list_of_tickers)) 128 | except Exception as e: 129 | print(e) 130 | 131 | print("\n\n\n\n--- this took %s seconds to run ---" % 132 | (time.time() - start_time)) 133 | 134 | return positive_scans 135 | 136 | 137 | if __name__ == '__main__': 138 | mainObj().main_func() 139 | -------------------------------------------------------------------------------- /dynamic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Unusual Volume Scanner 7 | 8 | 9 | 10 | 16 | 21 | 26 | 27 | 28 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
51 |

Unusual Volume Scanner

52 |

53 | Get alerted when a stock's volume exceeds 10 standard deviations from the mean 54 | within the last 3 days. Stay ahead of other retailers. 55 |

56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 85 | 86 | 87 | 88 | 89 | 95 | 96 | 97 | 98 | 99 | 100 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
TickerDateVolume
68 | AIMT 72 |
79 | AKCA 83 |
90 | NSYS 94 |
101 | UTSI 105 |
116 | 117 | 118 | 119 | Made by Sam Pomerantz and contributors. 120 | 121 | 122 | 123 | 124 | GitHub 125 | 126 | 127 | • 128 | 129 | 130 | LinkedIn 131 | 132 | 133 | • 134 | 135 | 136 | Donate 137 | 138 | 139 | 140 | 141 | Thank you for visiting 142 | 143 | 144 |
145 | 146 | 147 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Unusual Volume Scanner 7 | 8 | 9 | 15 | 20 | 25 | 26 | 27 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
50 |

Unusual Volume Scanner

51 |

52 | Get alerted when a stock's volume exceeds 10 standard deviations from the mean 53 | within the last 3 days. Dynamic Version. 54 |

55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 95 | 96 | 97 | 98 | 99 | 105 | 106 | 107 | 108 | 109 | 110 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
TickerDateVolume
67 | SRRK 71 | 2024-10-0742,667,700
78 | DOCU 82 | 2024-10-1033,208,400
89 | FANG 93 | 2024-10-0810,794,600
100 | ALRS 104 | 2024-10-091,002,800
111 | EXEEZ 115 | 2024-10-10108,200
126 | 127 | 128 | 129 | Made by Sam Pomerantz and contributors. 130 | 131 | 132 | 133 | 134 | GitHub 135 | 136 | 137 | • 138 | 139 | 140 | LinkedIn 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | Thank you for visiting 149 | 150 | 151 |
152 | 153 | 154 | --------------------------------------------------------------------------------