Skip to content

Instantly share code, notes, and snippets.

@alexnsolo
Last active May 14, 2020 20:48
Show Gist options
  • Save alexnsolo/8c80aec9dbdd94171f18f53964d03dda to your computer and use it in GitHub Desktop.
Save alexnsolo/8c80aec9dbdd94171f18f53964d03dda to your computer and use it in GitHub Desktop.
Intrinio API Ruby Script for Historical Insider Ownership
###########################################################################
# 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