Last active
May 14, 2020 20:48
-
-
Save alexnsolo/8c80aec9dbdd94171f18f53964d03dda to your computer and use it in GitHub Desktop.
Intrinio API Ruby Script for Historical Insider Ownership
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
########################################################################### | |
# Get Historical Insider Ownership for a Company on a Given Date | |
########################################################################### | |
require 'net/http' | |
require 'json' | |
require 'date' | |
#-------------------------------------------------------------------------- | |
# Variables | |
#-------------------------------------------------------------------------- | |
# The Intrinio APIs | |
@api_v1_url = "https://api.intrinio.com" | |
@api_v2_url = "https://api-v2.intrinio.com" | |
# Enter your own API key here | |
@api_key = "{YOUR_API_KEY}" | |
# The ticker of the company | |
@ticker = "EQ" | |
# The date at which point we want to calculate insider ownership for a company | |
@effective_date = Date.parse("2020-01-01") | |
#-------------------------------------------------------------------------- | |
# Helper Methods | |
#-------------------------------------------------------------------------- | |
# Helper method to get a single page of API data | |
def get_api_data(endpoint, params, api_url: @api_v1_url) | |
params[:api_key] = @api_key | |
uri = URI("#{api_url}#{endpoint}") | |
uri.query = URI.encode_www_form(params) | |
res = Net::HTTP.get_response(uri) | |
JSON.parse(res.body) | |
end | |
# Helper method to get all pages of data for an API call | |
def get_all_paged_api_v1_data(endpoint, params) | |
data = [] | |
page = 1 | |
while true | |
params[:page] = page | |
response = get_api_data(endpoint, params, api_url: @api_v1_url) | |
data.push(*response["data"]) | |
break if response["current_page"] == response["total_pages"] | |
page += 1 | |
end | |
data | |
end | |
# Helper method to get all insider transactions for a given ticker | |
def get_all_insider_transactions(ticker) | |
endpoint = "/companies/insider_transactions" | |
params = { | |
identifier: ticker | |
} | |
get_all_paged_api_v1_data(endpoint, params) | |
end | |
def get_stock_price_as_of_date(ticker, date: Date.today) | |
endpoint = "/prices" | |
params = { | |
ticker: ticker, | |
page_size: 1, | |
end_date: date | |
} | |
response = get_api_data(endpoint, params) | |
response["data"].first["adj_close"] | |
end | |
def get_cumulative_split_factor(ticker, start_date: nil, end_date: Date.today) | |
endpoint = "/securities/#{ticker}/prices/adjustments" | |
params = { | |
page_size: 10000, | |
start_date: start_date, | |
end_date: end_date | |
} | |
response = get_api_data(endpoint, params, api_url: @api_v2_url) | |
adjustments = response["stock_price_adjustments"] | |
adjustments.filter!{|x| x["split_ratio"] != 1.0} | |
split_factor = adjustments.map{|x| x["split_ratio"]}.inject(:*) | |
split_factor ||= 1.0 | |
split_factor | |
end | |
#-------------------------------------------------------------------------- | |
# Logic | |
#-------------------------------------------------------------------------- | |
# Get all insider transactions for the ticker up until the given historical date | |
transactions = get_all_insider_transactions(@ticker) | |
# Fill in effective date with transaction date or filing date | |
transactions.each{|x| x["effective_date"] = x["transaction_date"] || x["filing_date"]} | |
# Sort by transaction date desc | |
transactions.sort_by!{|x| Date.parse(x["effective_date"])}.reverse! | |
# Filter out transactions after the historical date | |
transactions.filter!{|x| Date.parse(x["effective_date"]) <= @effective_date} | |
# Filter out derivative transactions | |
transactions.filter!{|x| x["derivative_transaction"] == false} | |
# Make a map of owners | |
owners = {} | |
transactions.each do |transaction| | |
key = transaction["owner_cik"] | |
owners[key] ||= {} | |
owners[key]["owner_cik"] ||= transaction["owner_cik"] | |
owners[key]["owner_name"] ||= transaction["owner_name"] | |
owners[key]["last_reported_date"] ||= transaction["filing_date"] | |
owners[key]["value"] ||= 0 | |
owners[key]["amount"] ||= 0 | |
owners[key]["indirect_ownerships"] ||= {} | |
end | |
# Tally up direct ownership | |
transactions.filter{|x| x["ownership_type_code"] == "D"}.each do |transaction| | |
key = transaction["owner_cik"] | |
owners[key]["direct_ownership"] ||= transaction["total_shares_owned"] | |
end | |
# Tally up indirect ownership, grouping by nature of ownership | |
transactions.filter{|x| x["ownership_type_code"] == "I"}.each do |transaction| | |
key = transaction["owner_cik"] | |
ownership_key = transaction["nature_of_ownership"] | |
owners[key]["indirect_ownerships"][ownership_key] ||= transaction["total_shares_owned"] | |
end | |
# Aggregate ownership amounts | |
owners.values.each do |owner| | |
owner["amount"] += owner["direct_ownership"] || 0 | |
owner["amount"] += owner["indirect_ownerships"].values.sum | |
owner.delete("direct_ownership") | |
owner.delete("indirect_ownerships") | |
end | |
# Calculate ownership value using the stock price and adjusting for splits as of the effective date | |
stock_price = get_stock_price_as_of_date(@ticker, date: @effective_date) | |
owners.values.each do |owner| | |
split_factor = get_cumulative_split_factor(@ticker, start_date: owner["last_reported_date"], end_date: @effective_date) | |
owner["value"] = owner["amount"] * stock_price / split_factor | |
end | |
puts owners.values.sort_by{|x| x["amount"]}.reverse |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment