Dmitry Beresnev commited on
Commit
59845f6
·
1 Parent(s): 13212b8

fix the requests to the FMP API

Browse files
src/api/insiders/insider_trading_aggregator.py CHANGED
@@ -111,68 +111,75 @@ class InsiderTradingAggregator:
111
 
112
  async def get_fmp_insider_trades(self, session: aiohttp.ClientSession,
113
  symbol: str, limit: int = 100, filter_days: int = 30) -> List[InsiderTrade]:
114
- """Get insider trades from Financial Modeling Prep API"""
 
 
 
 
 
 
 
 
115
  all_trades = []
116
- page = 0
117
-
118
- start_date = datetime.now() - timedelta(days=filter_days)
119
-
120
- # IMPROVEMENT: Loop through pages to fetch all available data.
121
- keep_fetching = True
122
- while keep_fetching:
123
- endpoint = "insider-trading"
124
- params = {'symbol': symbol, 'limit': limit, 'page': page}
125
-
126
- logger.info(f"Requesting FMP data for {symbol}, page {page}...")
127
- data = await self._make_request(session, 'fmp', endpoint, params)
128
-
129
- if not data:
130
- break # Stop if API returns no more data
131
-
132
- for trade in data:
133
- # IMPROVEMENT: Check the transaction date. If it's too old, stop fetching.
134
- trade_date_str = trade.get('transactionDate')
135
- try:
136
- trade_date = datetime.strptime(trade_date_str, '%Y-%m-%d %H:%M:%S')
137
- if trade_date < start_date:
138
- keep_fetching = False
139
- # Don't process this trade, and signal to stop fetching more pages
140
- break
141
- except (ValueError, TypeError):
142
- # If date is invalid, we can't check it, so we process it but log a warning
143
- logger.warning(f"Could not parse date for FMP trade: {trade_date_str}")
144
-
145
- try:
146
- disposition = (trade.get('acquistionOrDisposition') or '').upper()
147
- trans_type = TransactionType.BUY.value if disposition == 'A' else TransactionType.SELL.value
148
-
149
- shares = int(trade.get('securitiesTransacted', 0) or 0)
150
- price = float(trade.get('price', 0) or 0)
151
-
152
- insider_trade = InsiderTrade(
153
- symbol=trade.get('symbol', ''),
154
- company_name=trade.get('companyName', ''),
155
- insider_name=trade.get('reportingName', ''),
156
- position=trade.get('typeOfOwner', ''),
157
- transaction_date=trade.get('transactionDate', ''),
158
- transaction_type=trans_type,
159
- shares=shares,
160
- price=price,
161
- value=float(shares * price),
162
- form_type=trade.get('formType', ''),
163
- source='FMP',
164
- filing_date=trade.get('filingDate', ''),
165
- ownership_type=trade.get('directOrIndirectOwnership', '')
166
- )
167
- all_trades.append(insider_trade)
168
- except (ValueError, TypeError) as e:
169
- logger.warning(f"Error parsing FMP trade data for {symbol}: {e} -> {trade}")
170
- continue
171
-
172
- page += 1
173
- await asyncio.sleep(0.2)
174
-
175
- logger.info(f"Retrieved {len(all_trades)} recent trades from FMP for {symbol}.")
176
  return all_trades
177
 
178
  async def get_sec_api_insider_trades(self, session: aiohttp.ClientSession,
 
111
 
112
  async def get_fmp_insider_trades(self, session: aiohttp.ClientSession,
113
  symbol: str, limit: int = 100, filter_days: int = 30) -> List[InsiderTrade]:
114
+ """
115
+ Get insider trades from Financial Modeling Prep API
116
+ Get insider trades from FMP by iterating day-by-day for a specific period.
117
+ NOTE: This method is inefficient and makes many API calls.
118
+ """
119
+ if filter_days > 14:
120
+ logger.warning(f"FMP date range capped at 14 days. Reducing from {filter_days} to 14.")
121
+ filter_days = 14
122
+
123
  all_trades = []
124
+ today = datetime.now()
125
+ date_range = [today - timedelta(days=i) for i in range(filter_days)]
126
+
127
+ # Outer Loop: Iterate through each day in the specified range.
128
+ for single_date in date_range:
129
+ page = 0
130
+ date_str = single_date.strftime('%Y-%m-%d')
131
+
132
+ # Inner Loop: Handle pagination for the current day.
133
+ while True:
134
+ endpoint = "insider-trading"
135
+ params = {
136
+ 'symbol': symbol,
137
+ 'date': date_str,
138
+ 'page': page,
139
+ 'limit': limit
140
+ }
141
+
142
+ logger.info(f"Requesting FMP data for {symbol} on {date_str}, page {page}...")
143
+ data = await self._make_request(session, 'fmp', endpoint, params)
144
+
145
+ # If no data is returned for this page, we're done with this date.
146
+ if not data:
147
+ break
148
+
149
+ for trade in data:
150
+ try:
151
+ disposition = (trade.get('acquistionOrDisposition') or '').upper()
152
+ trans_type = TransactionType.BUY.value if disposition == 'A' else TransactionType.SELL.value
153
+
154
+ shares = int(trade.get('securitiesTransacted', 0) or 0)
155
+ price = float(trade.get('price', 0) or 0)
156
+
157
+ insider_trade = InsiderTrade(
158
+ symbol=trade.get('symbol', ''),
159
+ company_name=trade.get('companyName', ''),
160
+ insider_name=trade.get('reportingName', ''),
161
+ position=trade.get('typeOfOwner', ''),
162
+ transaction_date=trade.get('transactionDate', ''),
163
+ transaction_type=trans_type,
164
+ shares=shares,
165
+ price=price,
166
+ value=float(shares * price),
167
+ form_type=trade.get('formType', ''),
168
+ source='FMP',
169
+ filing_date=trade.get('filingDate', ''),
170
+ ownership_type=trade.get('directOrIndirectOwnership', '')
171
+ )
172
+ all_trades.append(insider_trade)
173
+ except (ValueError, TypeError) as e:
174
+ logger.warning(f"Error parsing FMP trade data for {symbol}: {e} -> {trade}")
175
+ continue
176
+
177
+ # Increment the page to fetch the next set of results for the same day.
178
+ page += 1
179
+ await asyncio.sleep(0.2) # Courteous delay
180
+
181
+ logger.info(
182
+ f"Retrieved {len(all_trades)} trades from FMP for {symbol} by iterating through {len(date_range)} days.")
 
183
  return all_trades
184
 
185
  async def get_sec_api_insider_trades(self, session: aiohttp.ClientSession,