This post will cover
-
Checking filings for multiple tickers at once
-
Isolating filings from today only
-
Emailing the filing links to yourself
Setup: Tickers and CIKs
On your computer open PowerShell.
Existing Variables
We will be reusing some variables from Part 1, lets start typing those in to store them in memory:
$fromEmail = 'youremailaddress@gmail.com'
Recommending GMail because my example will need a GMail app password (will go over this further down).
And since we're emailing:
$toEmail = 'yoursecondemail@address.com'
I recommend a separate second email because sometimes if you email yourself the notifications don't show up on your phone, YMMV, making them both the same won't break the script.
API Headers:
$hdrs = @{"User-Agent" = "personal use $fromEmail"}
API URI:
$tickerSite = 'https://www.sec.gov/files/company_tickers.json'
Call the API to get ticker CIKs for all companies:
$tickerResponse = Invoke-RestMethod -Headers $hdrs -uri $tickerSite
SEC Website
$secWebsite = 'https://www.sec.gov/Archives/edgar/data'
Create a List of Tickers
This will build on the "Find the ticker's CIK" section in part 1. Open Notepad on your computer. Enter a ticker, then create a new line for each ticker.
My Notepad ticker list will be:
TMPO DKDCA
but you can add as many as you like, as long as they are each on their own line. I know for today (Aug 4 2023) each of those have filings (3 and 2 respectively) so we should expect 5 email alerts when we're done. For a list of companies who have submitted filings for today check latest filings.
In Notepad, click the File menu, then Save As... give it a name (doesn't matter what you name it) but make sure it ends in .csv. Mine is called tickers.csv
https://preview.redd.it/diy-filing-alerts-part-2-emailing-todays-filings-v0-15g3kim7t4gb1.png?width=261&format=png&auto=webp&s=c1cfa711d75b9c7be38b705e18aa7cf34132327cTake note of the folder you've saved this csv file in. Mine is C:\temp
https://preview.redd.it/diy-filing-alerts-part-2-emailing-todays-filings-v0-jvcuoqiit4gb1.png?width=391&format=png&auto=webp&s=f3cf97cbd3c1284f7fa1839fc21cbcaaa9269f64Save the folder in a variable:
$folder = 'c:\temp'
Make sure there are no other CSV files in this folder.
Import Tickers into PowerShell
Our CSV will need a header (read: column title):
$csvHeader = 'Ticker'
Next we will import the tickers in our list and add the header:
$csv = Import-Csv -Path $folder\*.CSV -Header $csvHeader
To make it easier for our script to manipulate data, we are going to load each ticker into a special list called a hashset. Store the hashset into a variable:
$tickerList = [Collections.Generic.Hashset[String]]@()
The hashset $tickerList is empty so we will instruct PowerShell to add each ticker from our CSV into it:
foreach ($item in $csv) { $tickerList.Add($item.ticker) }
The above code reads: for each item in our CSV, add it to the tickerList hashset. If there are two tickers like in my example, the code will run through twice, if there are three, thrice, four times, not sure of the word for that, and so on...
Find a CIK for each Ticker
Refer to "Find the ticker's CIK" in part 1 if you need, because we will be building off that.
Manipulate the $tickerResponse variable so its in a format we can use to match row number, to ticker, to CIK:
$responseNumbers = ($tickerResponse | Get-Member | Where-Object {$_.membertype -eq 'NoteProperty'}).Name
Make a new hashset to store the CIKs:
$cikList = [Collections.Generic.Hashset[String]]@()
We will use the same code from part 1 but since we have multiple tickers we will put that inside of another foreach, but then also add the found CIK into the hashset:
foreach ($number in $responseNumbers) { foreach ($ticker in $tickerList) { if ($tickerResponse.$number.ticker -eq $ticker) { $cik_str = ($tickerResponse.$number).cik_str.ToString().padLeft(10,'0') $cikList.Add($cik_str) } } }
Optionally, check $ciklist to see what you got:
PS C:\> $ciklist 0001849380 0001813658
Grab All the Filings
Per the SEC API documentation
Each entity’s current filing history is available at the following URL:
https://data.sec.gov/submissions/CIK**##########**.json
Where the ########## is the entity’s 10-digit Central Index Key (CIK), including leading zeros.
We'll use that link (with the $cik variable replacing the # symbols) as our API URI.
Before we write the code I like to think through what's happening...
-
Each ticker will need its own filing response
-
We only want filings from today
-
Since we're checking for filings all day, we don't want filings we've already been alerted about
-
Lastly email the filings
Lets think of the bullet points in terms of code. No need to type in these code blocks for this section. I'm just using them so you can recognize the individual pieces when we put all the bullets together.
-
We have a $cikList full of $ciks
-
foreach $cik, create a $filingUri and call the API for a $resonse, but only if the company has filings dated today (we'll use the variable $logDate to represent today)
foreach ($cik in $cikList) { $filingUri = "https://data.sec.gov/submissions/CIK$cik.json" $response = Invoke-RestMethod -Headers $hdrs -uri $filingUri | Where-Object {$_.filings.recent.filingDate -like "*$logDate*"} Start-Sleep -Milliseconds 100 # space for other bullet points }
Note: I've added the Start-Sleep line becuase the SEC says we're only allowed to check 10 CIKs per second; we have to purposely slow down the script otherwise they will block us.
-
the $response is only usable to us if it includes filings from today's date, otherwise skip it
if ($response) { # space for bullet points }
-
if there are truly $responses where the $logdate is today's date then count how many their are
$i = ( $response.filings.recent.filingDate | Group-Object | Where-Object { $_.Name -eq $logDate } ).Count
Note: $i represents the total number of filings (an integer)
-
Start with the first filing (remember 0 is the first filing, 1 is the second...)
$n = 0
-
Determine a unique identifier - I'm using the filing's Accession Number which we know how to get from part 1
$accessionNumber = $response.filings.recent.accessionNumber[$n] -replace '[-]',''
-
create a hashset to store the Accession Numbers after we've emailed them
$spacFilings = [Collections.Generic.Hashset[PSCustomObject]]@()
-
check to see if the accession number is already in the spacFilings hashset, if the hashset does not contain it, grab the recent filings and move on to the next bullet point. If it does, skip this filing
if ($spacFilings.accessionNumber -notcontains $accessionNumber) { $recent = $response.filings.recent # object will go here }
-
create an $obj for the first filing, then the next. See "Build a Custom Object" in part 1 for more of an explanation on building the object
$obj = [PSCustomObject]@{ Name = $response.name Tickers = $response.tickers[0] AccessionNumber = $accessionNumber FilingDate = $recent.filingDate[$n] Form = ($recent).form[$n] Link = $secWebsite + '/' + $cik + '/' + $accessionNumber + '/' + $recent.primaryDocument[$n] }
-
email the filing
-
We will need some new variables to send a gmail, create an app password and write it down for later. We'll type the variables in a few minutes.
-
create the email using data from the above $obj
-
$subject = "$($obj.tickers) | New SEC filing" $body = "Name: $($obj.name) `n`nTicker: $($obj.tickers) `n`nFiling Date: $($obj.filingdate) `n`nForm: $($obj.form) `n`nLink: $($obj.link)"
-
send the email
Send-MailMessage -From $fromEmail -To $toEmail -Subject $subject -Body $body -SmtpServer 'smtp.gmail.com' -Port 587 -UseSsl -Credential $credential
-
do this until we've looped through however many new filings we've found
do { # bullet points go here } until ( $n -ge $i )
Note "ge" stands for greater than or equal.
Putting all the Bullet Points of Code Together
Before we combine the above lines, we need to add some variables. Now you can type them.
Today's date:
$logDate = Get-Date -Format yyyy-MM-dd
Filings hashset:
$spacFilings = [Collections.Generic.Hashset[PSCustomObject]]@()
GMail App Password
$pass = ConvertTo-SecureString -String 'App Password with no hyphens goes between single quotes' -AsPlainText -Force
GMail credential
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $fromEmail, $pass
Set security protocols for sending (this may or may not be needed but we're adding it anyway):
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12, [Net.SecurityProtocolType]::Tls11
Bullet points combined into script:
foreach ($cik in $cikList) { $n = 0 $filingUri = "https://data.sec.gov/submissions/CIK$cik.json" $response = Invoke-RestMethod -Headers $hdrs -uri $filingUri | Where-Object {$_.filings.recent.filingDate -like "*$logDate*"} Start-Sleep -Milliseconds 100 # do the thing only if there are filings from today if ($response) { # determine number of filings with today's date $i = ( $response.filings.recent.filingDate | Group-Object | Where-Object { $_.Name -eq $logDate } ).Count # collect filings from today do { $accessionNumber = $response.filings.recent.accessionNumber[$n] -replace '[-]','' # Check if Filing was Already Checked if ($spacFilings.accessionNumber -notcontains $accessionNumber) { $recent = $response.filings.recent $obj = [PSCustomObject]@{ Name = $response.name Tickers = $response.tickers[0] AccessionNumber = $accessionNumber FilingDate = $recent.filingDate[$n] Form = ($recent).form[$n] Link = $secWebsite + '/' + $cik + '/' + $accessionNumber + '/' + $recent.primaryDocument[$n] } # create / send email $subject = "$($obj.tickers) | New SEC filing" $body = "Name: $($obj.name) `n`nTicker: $($obj.tickers) `n`nFiling Date: $($obj.filingdate) `n`nForm: $($obj.form) `n`nLink: $($obj.link)" Send-MailMessage -From $fromEmail -To $toEmail -Subject $subject -Body $body -SmtpServer 'smtp.gmail.com' -Port 587 -UseSsl -Credential $credential # add to daily record [void]$spacFilings.Add($obj) $n++ } else {$n++} } until ( $n -ge $i ) } }
Check your email and there should be five alerts!
If you run the above code block again, no alerts because it checks the $accessionNumber hashset and sees that the filing has been emailed already.
Script
The full script is below, I've organized it so the variables are on top. Remember to change your email addresses and your GMail app password before you enter:
$fromEmail = 'youremail@gmail.com' $toEmail = 'youremail@address.com' $hdrs = @{"User-Agent" = "personal use $fromEmail"} $tickerSite = 'https://www.sec.gov/files/company_tickers.json' $tickerResponse = Invoke-RestMethod -Headers $hdrs -uri $tickerSite $secWebsite = 'https://www.sec.gov/Archives/edgar/data' $folder = 'c:\temp' $csvHeader = 'Ticker' $csv = Import-Csv -Path $folder\*.CSV -Header $csvHeader $tickerList = [Collections.Generic.Hashset[String]]@() $cikList = [Collections.Generic.Hashset[String]]@() $logDate = Get-Date -Format yyyy-MM-dd $spacFilings = [Collections.Generic.Hashset[PSCustomObject]]@() $secWebsite = 'https://www.sec.gov/Archives/edgar/data' $pass = ConvertTo-SecureString -String 'app password' -AsPlainText -Force $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $fromEmail, $pass [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12, [Net.SecurityProtocolType]::Tls11 foreach ($item in $csv) { $tickerList.Add($item.ticker) } foreach ($number in $responseNumbers) { foreach ($ticker in $tickerList) { if ($tickerResponse.$number.ticker -eq $ticker) { $cik_str = ($tickerResponse.$number).cik_str.ToString().padLeft(10,'0') $cikList.Add($cik_str) } } } $responseNumbers = ($tickerResponse | Get-Member | Where-Object {$_.membertype -eq 'NoteProperty'}).Name # Check if CIK has filings dated today foreach ($cik in $cikList) { $n = 0 $filingUri = "https://data.sec.gov/submissions/CIK$cik.json" $response = Invoke-RestMethod -Headers $hdrs -uri $filingUri | Where-Object {$_.filings.recent.filingDate -like "*$logDate*"} Start-Sleep -Milliseconds 100 # do the thing only if there are filings from today if ($response) { # determine number of filings with today's date $i = ( $response.filings.recent.filingDate | Group-Object | Where-Object { $_.Name -eq $logDate } ).Count # collect filings from today do { $accessionNumber = $response.filings.recent.accessionNumber[$n] -replace '[-]','' # Check if Filing was Already Checked if ($spacFilings.accessionNumber -notcontains $accessionNumber) { $recent = $response.filings.recent $obj = [PSCustomObject]@{ Name = $response.name Tickers = $response.tickers[0] AccessionNumber = $accessionNumber FilingDate = $recent.filingDate[$n] Form = ($recent).form[$n] Link = $secWebsite + '/' + $cik + '/' + $accessionNumber + '/' + $recent.primaryDocument[$n] } # create / send email $subject = "$($obj.tickers) | New SEC filing" $body = "Name: $($obj.name) `n`nTicker: $($obj.tickers) `n`nFiling Date: $($obj.filingdate) `n`nForm: $($obj.form) `n`nLink: $($obj.link)" Send-MailMessage -From $fromEmail -To $toEmail -Subject $subject -Body $body -SmtpServer 'smtp.gmail.com' -Port 587 -UseSsl -Credential $credential # add to daily record [void]$spacFilings.Add($obj) $n++ } else {$n++} } until ( $n -ge $i ) } }
Next...
The final part will include how to:
-
have the script run between the filing window hours
-
automate this to run everyday
Til then and thanks for reading, questions let me know