1 """Scrape insider trade information from `holdings.nasdaq.com`
8 from BeautifulSoup import BeautifulSoup
10 from insider.models import add_transaction
15 # Convert NASDAQ's people (generaly LAST FIRST[ MIDDLE]) to
16 # Person.name. This is for hardcoded overrides when the
17 # default algorithm fails.
19 'relation': {'OFF': 'officer', 'DIR': 'Director'},
22 'AS': 'Automatic Sell',
23 'AB': 'Automatic Buy',
24 'JS': 'Disposition (Non Open Market)',
25 'JB': 'Acquisition (Non Open Market)',
26 'OE': 'Option Exercise',
37 def get_trades(ticker):
38 exchange_symbol,company_symbol = ticker.split(':')
39 url = 'http://holdings.nasdaq.com/asp/Form4.asp?selected={}'.format(
41 html = urllib2.urlopen(url).read()
42 soup = BeautifulSoup(html)
43 table = soup('table', {'class': 'holdings', 'width': '100%'})[1]
44 for row in table('tr'):
48 data = {'exchange': 'UNKNOWN', 'exchange_symbol': exchange_symbol,
49 'company': 'UNKNOWN', 'company_symbol': company_symbol,
51 for i,(field,class_) in enumerate([
52 (None, None), # first column is blank
53 ('person', 'Holddata'),
54 ('relation', 'Holddata'),
57 ('transaction', 'Holddata'),
58 ('ownership', 'Holddata'),
59 ('shares traded', 'Holdnum'),
60 ('last price', 'Holdnum'),
61 ('shares held', 'Holdnum'),
65 break # not enough columns in this row
67 continue # nothing interesting in this field
70 break # we made it to the end of the list
71 if dict(tds[i].attrs).get('class', None) != class_:
74 if class_ == 'Holdnum': # decode numerical values
78 value = float(value.strip('$()').replace(',', ''))
79 elif field in CODE: # decode abbreviated values
84 if field == 'person': # fall back to default name
85 last,first_plus = value.title().split(' ', 1)
86 value = ' '.join((first_plus, last))
88 print('unknown code {} for {} field\n{}'.format(
92 value = datetime.datetime.strptime(value, '%m/%d/%Y')
96 if data['transaction'] and 'Non Open Market' in data['transaction']:
97 data['exchange'] = 'non-open market'
98 data['exchange_symbol'] = '-'
99 if data['shares traded'] and data['transaction']:
100 for key in ['sell', 'option', 'disposition']:
101 if key in data['transaction'].lower():
102 data['shares traded'] *= -1 # - for selling
103 # estimate total price
104 price = data['last price'] or 0
105 shares = data['shares traded'] or 0
106 value = decimal.Decimal(price * shares)
107 data['value'] = value.quantize(decimal.Decimal('.01'))
111 if __name__ == '__main__':
116 for ticker in sys.argv[1:]:
117 for trade in get_trades(ticker=ticker):
119 # display the trade we're looking at
120 for field,value in sorted(trade.items()):
121 print('{}\t{}'.format(field, value))
124 person=trade['person'],
126 exchange=trade['exchange'],
127 exchange_symbol=trade['exchange_symbol'],
128 company=trade['company'],
129 company_symbol=trade['company_symbol'],
130 shares=trade['shares traded'],
131 value=trade['value'],
132 source=trade['source'],