form4api 0.1.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {form4api-0.1.0 → form4api-0.3.0}/PKG-INFO +104 -104
  2. {form4api-0.1.0 → form4api-0.3.0}/README.md +1 -1
  3. {form4api-0.1.0 → form4api-0.3.0}/form4api/_types.py +7 -0
  4. {form4api-0.1.0 → form4api-0.3.0}/form4api/_webhook_utils.py +3 -3
  5. {form4api-0.1.0 → form4api-0.3.0}/form4api/resources/__init__.py +3 -3
  6. {form4api-0.1.0 → form4api-0.3.0}/form4api/resources/_insiders.py +11 -0
  7. form4api-0.3.0/form4api/resources/_signals.py +57 -0
  8. {form4api-0.1.0 → form4api-0.3.0}/form4api/resources/_transactions.py +5 -1
  9. {form4api-0.1.0 → form4api-0.3.0}/form4api.egg-info/PKG-INFO +104 -104
  10. {form4api-0.1.0 → form4api-0.3.0}/pyproject.toml +1 -1
  11. {form4api-0.1.0 → form4api-0.3.0}/setup.cfg +4 -4
  12. {form4api-0.1.0 → form4api-0.3.0}/tests/test_client.py +19 -0
  13. form4api-0.1.0/form4api/resources/_signals.py +0 -29
  14. {form4api-0.1.0 → form4api-0.3.0}/LICENSE +0 -0
  15. {form4api-0.1.0 → form4api-0.3.0}/form4api/__init__.py +0 -0
  16. {form4api-0.1.0 → form4api-0.3.0}/form4api/_client.py +0 -0
  17. {form4api-0.1.0 → form4api-0.3.0}/form4api/_errors.py +0 -0
  18. {form4api-0.1.0 → form4api-0.3.0}/form4api/resources/_companies.py +0 -0
  19. {form4api-0.1.0 → form4api-0.3.0}/form4api/resources/_webhooks.py +0 -0
  20. {form4api-0.1.0 → form4api-0.3.0}/form4api.egg-info/SOURCES.txt +0 -0
  21. {form4api-0.1.0 → form4api-0.3.0}/form4api.egg-info/dependency_links.txt +0 -0
  22. {form4api-0.1.0 → form4api-0.3.0}/form4api.egg-info/requires.txt +0 -0
  23. {form4api-0.1.0 → form4api-0.3.0}/form4api.egg-info/top_level.txt +0 -0
@@ -1,104 +1,104 @@
1
- Metadata-Version: 2.4
2
- Name: form4api
3
- Version: 0.1.0
4
- Summary: Python client for the Form4API — real-time SEC Form 4 insider trading data
5
- License-Expression: MIT
6
- Project-URL: Homepage, https://form4api.com
7
- Project-URL: Documentation, https://form4api.com/docs
8
- Project-URL: Repository, https://github.com/theodor90/form4api-py.git
9
- Requires-Python: >=3.11
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Requires-Dist: httpx>=0.27
13
- Provides-Extra: dev
14
- Requires-Dist: pytest>=8; extra == "dev"
15
- Requires-Dist: pytest-httpx>=0.30; extra == "dev"
16
- Requires-Dist: respx>=0.21; extra == "dev"
17
- Dynamic: license-file
18
-
19
- # form4api
20
-
21
- Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
22
-
23
- Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
24
-
25
- ## Installation
26
-
27
- ```bash
28
- pip install form4api
29
- ```
30
-
31
- ## Sync quickstart
32
-
33
- ```python
34
- from form4api import Form4ApiClient
35
-
36
- client = Form4ApiClient("YOUR_API_KEY")
37
-
38
- # Recent open-market purchases at Apple
39
- txns = client.transactions.list(ticker="AAPL", code="P", per_page=5)
40
- for t in txns:
41
- print(t.insider_name, t.shares_amount, "@", t.price_per_share)
42
-
43
- # Company overview
44
- company = client.companies.get("MSFT")
45
- print(company.name, company.active_insiders, "active insiders")
46
-
47
- # Insider detail
48
- insider = client.insiders.get("0001234567")
49
- print(insider.name, insider.officer_title)
50
-
51
- # Cluster-buy signals (Business plan)
52
- signals = client.signals.list(ticker="NVDA")
53
- for sig in signals:
54
- if sig.is_cluster_buy:
55
- print(sig.company_name, sig.insider_count, "buyers")
56
- ```
57
-
58
- ## Async quickstart
59
-
60
- ```python
61
- import asyncio
62
- from form4api import AsyncForm4ApiClient
63
-
64
- async def main():
65
- async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
66
- txns = await client.transactions.list(ticker="AAPL", per_page=5)
67
- for t in txns:
68
- print(t.insider_name, t.shares_amount, "@", t.price_per_share)
69
-
70
- asyncio.run(main())
71
- ```
72
-
73
- ## Resources
74
-
75
- | Resource | Methods |
76
- |---|---|
77
- | `client.transactions` | `.list(**params)`, `.paginate(**params)` |
78
- | `client.insiders` | `.get(cik)`, `.transactions(cik, **params)` |
79
- | `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
80
- | `client.signals` | `.list(**params)` — Business plan |
81
- | `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
82
-
83
- ## Error handling
84
-
85
- ```python
86
- from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
87
-
88
- client = Form4ApiClient("YOUR_API_KEY")
89
-
90
- try:
91
- signals = client.signals.list()
92
- except PlanError as e:
93
- print(f"Upgrade to {e.required_plan}")
94
- except RateLimitError as e:
95
- print(f"Retry after {e.retry_after}s")
96
- except AuthError:
97
- print("Invalid API key")
98
- except NotFoundError:
99
- print("Resource not found")
100
- ```
101
-
102
- ## License
103
-
104
- MIT
1
+ Metadata-Version: 2.4
2
+ Name: form4api
3
+ Version: 0.3.0
4
+ Summary: Python client for the Form4API — real-time SEC Form 4 insider trading data
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://form4api.com
7
+ Project-URL: Documentation, https://form4api.com/docs
8
+ Project-URL: Repository, https://github.com/theodor90/form4api-py.git
9
+ Requires-Python: >=3.11
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: httpx>=0.27
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest>=8; extra == "dev"
15
+ Requires-Dist: pytest-httpx>=0.30; extra == "dev"
16
+ Requires-Dist: respx>=0.21; extra == "dev"
17
+ Dynamic: license-file
18
+
19
+ # form4api
20
+
21
+ Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
22
+
23
+ Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install form4api
29
+ ```
30
+
31
+ ## Sync quickstart
32
+
33
+ ```python
34
+ from form4api import Form4ApiClient
35
+
36
+ client = Form4ApiClient("YOUR_API_KEY")
37
+
38
+ # Recent open-market purchases at Apple
39
+ txns = client.transactions.list(ticker="AAPL", code="P", per_page=5)
40
+ for t in txns:
41
+ print(t.insider_name, t.shares_amount, "@", t.price_per_share)
42
+
43
+ # Company overview
44
+ company = client.companies.get("MSFT")
45
+ print(company.name, company.active_insiders, "active insiders")
46
+
47
+ # Insider detail
48
+ insider = client.insiders.get("0001234567")
49
+ print(insider.name, insider.officer_title)
50
+
51
+ # Cluster-buy signals (Business plan)
52
+ signals = client.signals.list(ticker="NVDA")
53
+ for sig in signals:
54
+ if sig.is_cluster_buy:
55
+ print(sig.company_name, sig.insider_count, "buyers")
56
+ ```
57
+
58
+ ## Async quickstart
59
+
60
+ ```python
61
+ import asyncio
62
+ from form4api import AsyncForm4ApiClient
63
+
64
+ async def main():
65
+ async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
66
+ txns = await client.transactions.list(ticker="AAPL", per_page=5)
67
+ for t in txns:
68
+ print(t.insider_name, t.shares_amount, "@", t.price_per_share)
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ## Resources
74
+
75
+ | Resource | Methods |
76
+ |---|---|
77
+ | `client.transactions` | `.list(**params)`, `.paginate(**params)` |
78
+ | `client.insiders` | `.search(name, **params)`, `.get(cik)`, `.transactions(cik, **params)` |
79
+ | `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
80
+ | `client.signals` | `.list(**params)` — Business plan |
81
+ | `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
82
+
83
+ ## Error handling
84
+
85
+ ```python
86
+ from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
87
+
88
+ client = Form4ApiClient("YOUR_API_KEY")
89
+
90
+ try:
91
+ signals = client.signals.list()
92
+ except PlanError as e:
93
+ print(f"Upgrade to {e.required_plan}")
94
+ except RateLimitError as e:
95
+ print(f"Retry after {e.retry_after}s")
96
+ except AuthError:
97
+ print("Invalid API key")
98
+ except NotFoundError:
99
+ print("Resource not found")
100
+ ```
101
+
102
+ ## License
103
+
104
+ MIT
@@ -57,7 +57,7 @@ asyncio.run(main())
57
57
  | Resource | Methods |
58
58
  |---|---|
59
59
  | `client.transactions` | `.list(**params)`, `.paginate(**params)` |
60
- | `client.insiders` | `.get(cik)`, `.transactions(cik, **params)` |
60
+ | `client.insiders` | `.search(name, **params)`, `.get(cik)`, `.transactions(cik, **params)` |
61
61
  | `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
62
62
  | `client.signals` | `.list(**params)` — Business plan |
63
63
  | `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
@@ -9,11 +9,18 @@ class Transaction:
9
9
  company_name: str
10
10
  insider_name: str
11
11
  insider_cik: str
12
+ insider_title: str | None
13
+ is_director: bool
14
+ is_officer: bool
15
+ is10_pct_owner: bool
12
16
  accession_number: str
13
17
  security_title: str
14
18
  transaction_code: str
19
+ is_open_market: bool
20
+ is10b5_plan: bool
15
21
  shares_amount: float
16
22
  price_per_share: float | None
23
+ total_value: float | None
17
24
  shares_owned_after: float | None
18
25
  direct_indirect: str | None
19
26
  is_derivative: bool
@@ -18,6 +18,6 @@ def verify_webhook(payload: str | bytes, signature: str, secret: str) -> bool:
18
18
  payload = payload.encode()
19
19
  expected = "sha256=" + hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
20
20
  return hmac.compare_digest(expected, signature)
21
-
22
-
23
-
21
+
22
+
23
+
@@ -11,6 +11,6 @@ __all__ = [
11
11
  "TransactionsResource",
12
12
  "WebhooksResource",
13
13
  ]
14
-
15
-
16
-
14
+
15
+
16
+
@@ -12,6 +12,17 @@ class InsidersResource:
12
12
  def __init__(self, client: Form4ApiClient) -> None:
13
13
  self._client = client
14
14
 
15
+ def search(
16
+ self,
17
+ name: str,
18
+ *,
19
+ page: int = 1,
20
+ per_page: int = 20,
21
+ ) -> list[Insider]:
22
+ params: dict[str, str] = {"name": name, "page": str(page), "per_page": str(per_page)}
23
+ data = self._client._get("/v1/insiders", params)
24
+ return [Insider(**item) for item in data]
25
+
15
26
  def get(self, cik: str) -> Insider:
16
27
  data = self._client._get(f"/v1/insiders/{cik}")
17
28
  return Insider(**data)
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Generator
4
+ from typing import TYPE_CHECKING
5
+
6
+ from form4api._types import InsiderSignal
7
+
8
+ if TYPE_CHECKING:
9
+ from form4api._client import Form4ApiClient
10
+
11
+
12
+ class SignalsResource:
13
+ def __init__(self, client: Form4ApiClient) -> None:
14
+ self._client = client
15
+
16
+ def list(
17
+ self,
18
+ *,
19
+ ticker: str | None = None,
20
+ cluster_buy: bool | None = None,
21
+ cluster_sell: bool | None = None,
22
+ page: int = 1,
23
+ per_page: int = 100,
24
+ ) -> list[InsiderSignal]:
25
+ params: dict[str, str] = {"page": str(page), "per_page": str(per_page)}
26
+ if ticker is not None:
27
+ params["ticker"] = ticker
28
+ if cluster_buy is not None:
29
+ params["cluster_buy"] = str(cluster_buy).lower()
30
+ if cluster_sell is not None:
31
+ params["cluster_sell"] = str(cluster_sell).lower()
32
+ data = self._client._get("/v1/signals", params)
33
+ return [InsiderSignal(**item) for item in data]
34
+
35
+ def paginate(
36
+ self,
37
+ *,
38
+ ticker: str | None = None,
39
+ cluster_buy: bool | None = None,
40
+ cluster_sell: bool | None = None,
41
+ per_page: int = 100,
42
+ ) -> Generator[list[InsiderSignal], None, None]:
43
+ page = 1
44
+ while True:
45
+ batch = self.list(
46
+ ticker=ticker, cluster_buy=cluster_buy, cluster_sell=cluster_sell,
47
+ page=page, per_page=per_page,
48
+ )
49
+ if not batch:
50
+ break
51
+ yield batch
52
+ if len(batch) < per_page:
53
+ break
54
+ page += 1
55
+
56
+
57
+
@@ -22,6 +22,7 @@ class TransactionsResource:
22
22
  code: str | None = None,
23
23
  from_date: str | None = None,
24
24
  to_date: str | None = None,
25
+ exclude_10b5: bool | None = None,
25
26
  page: int = 1,
26
27
  per_page: int = 50,
27
28
  ) -> list[Transaction]:
@@ -38,6 +39,8 @@ class TransactionsResource:
38
39
  params["from"] = from_date
39
40
  if to_date is not None:
40
41
  params["to"] = to_date
42
+ if exclude_10b5 is not None:
43
+ params["exclude_10b5"] = str(exclude_10b5).lower()
41
44
  data = self._client._get("/v1/transactions", params)
42
45
  return [Transaction(**item) for item in data]
43
46
 
@@ -50,6 +53,7 @@ class TransactionsResource:
50
53
  code: str | None = None,
51
54
  from_date: str | None = None,
52
55
  to_date: str | None = None,
56
+ exclude_10b5: bool | None = None,
53
57
  per_page: int = 50,
54
58
  ) -> Generator[list[Transaction], None, None]:
55
59
  page = 1
@@ -57,7 +61,7 @@ class TransactionsResource:
57
61
  batch = self.list(
58
62
  ticker=ticker, cik=cik, insider_cik=insider_cik,
59
63
  code=code, from_date=from_date, to_date=to_date,
60
- page=page, per_page=per_page,
64
+ exclude_10b5=exclude_10b5, page=page, per_page=per_page,
61
65
  )
62
66
  if not batch:
63
67
  break
@@ -1,104 +1,104 @@
1
- Metadata-Version: 2.4
2
- Name: form4api
3
- Version: 0.1.0
4
- Summary: Python client for the Form4API — real-time SEC Form 4 insider trading data
5
- License-Expression: MIT
6
- Project-URL: Homepage, https://form4api.com
7
- Project-URL: Documentation, https://form4api.com/docs
8
- Project-URL: Repository, https://github.com/theodor90/form4api-py.git
9
- Requires-Python: >=3.11
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Requires-Dist: httpx>=0.27
13
- Provides-Extra: dev
14
- Requires-Dist: pytest>=8; extra == "dev"
15
- Requires-Dist: pytest-httpx>=0.30; extra == "dev"
16
- Requires-Dist: respx>=0.21; extra == "dev"
17
- Dynamic: license-file
18
-
19
- # form4api
20
-
21
- Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
22
-
23
- Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
24
-
25
- ## Installation
26
-
27
- ```bash
28
- pip install form4api
29
- ```
30
-
31
- ## Sync quickstart
32
-
33
- ```python
34
- from form4api import Form4ApiClient
35
-
36
- client = Form4ApiClient("YOUR_API_KEY")
37
-
38
- # Recent open-market purchases at Apple
39
- txns = client.transactions.list(ticker="AAPL", code="P", per_page=5)
40
- for t in txns:
41
- print(t.insider_name, t.shares_amount, "@", t.price_per_share)
42
-
43
- # Company overview
44
- company = client.companies.get("MSFT")
45
- print(company.name, company.active_insiders, "active insiders")
46
-
47
- # Insider detail
48
- insider = client.insiders.get("0001234567")
49
- print(insider.name, insider.officer_title)
50
-
51
- # Cluster-buy signals (Business plan)
52
- signals = client.signals.list(ticker="NVDA")
53
- for sig in signals:
54
- if sig.is_cluster_buy:
55
- print(sig.company_name, sig.insider_count, "buyers")
56
- ```
57
-
58
- ## Async quickstart
59
-
60
- ```python
61
- import asyncio
62
- from form4api import AsyncForm4ApiClient
63
-
64
- async def main():
65
- async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
66
- txns = await client.transactions.list(ticker="AAPL", per_page=5)
67
- for t in txns:
68
- print(t.insider_name, t.shares_amount, "@", t.price_per_share)
69
-
70
- asyncio.run(main())
71
- ```
72
-
73
- ## Resources
74
-
75
- | Resource | Methods |
76
- |---|---|
77
- | `client.transactions` | `.list(**params)`, `.paginate(**params)` |
78
- | `client.insiders` | `.get(cik)`, `.transactions(cik, **params)` |
79
- | `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
80
- | `client.signals` | `.list(**params)` — Business plan |
81
- | `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
82
-
83
- ## Error handling
84
-
85
- ```python
86
- from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
87
-
88
- client = Form4ApiClient("YOUR_API_KEY")
89
-
90
- try:
91
- signals = client.signals.list()
92
- except PlanError as e:
93
- print(f"Upgrade to {e.required_plan}")
94
- except RateLimitError as e:
95
- print(f"Retry after {e.retry_after}s")
96
- except AuthError:
97
- print("Invalid API key")
98
- except NotFoundError:
99
- print("Resource not found")
100
- ```
101
-
102
- ## License
103
-
104
- MIT
1
+ Metadata-Version: 2.4
2
+ Name: form4api
3
+ Version: 0.3.0
4
+ Summary: Python client for the Form4API — real-time SEC Form 4 insider trading data
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://form4api.com
7
+ Project-URL: Documentation, https://form4api.com/docs
8
+ Project-URL: Repository, https://github.com/theodor90/form4api-py.git
9
+ Requires-Python: >=3.11
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: httpx>=0.27
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest>=8; extra == "dev"
15
+ Requires-Dist: pytest-httpx>=0.30; extra == "dev"
16
+ Requires-Dist: respx>=0.21; extra == "dev"
17
+ Dynamic: license-file
18
+
19
+ # form4api
20
+
21
+ Python client for [Form4API](https://form4api.com) — real-time SEC Form 4 insider trading data.
22
+
23
+ Supports Python 3.11+. Uses `httpx` for both sync and async HTTP.
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install form4api
29
+ ```
30
+
31
+ ## Sync quickstart
32
+
33
+ ```python
34
+ from form4api import Form4ApiClient
35
+
36
+ client = Form4ApiClient("YOUR_API_KEY")
37
+
38
+ # Recent open-market purchases at Apple
39
+ txns = client.transactions.list(ticker="AAPL", code="P", per_page=5)
40
+ for t in txns:
41
+ print(t.insider_name, t.shares_amount, "@", t.price_per_share)
42
+
43
+ # Company overview
44
+ company = client.companies.get("MSFT")
45
+ print(company.name, company.active_insiders, "active insiders")
46
+
47
+ # Insider detail
48
+ insider = client.insiders.get("0001234567")
49
+ print(insider.name, insider.officer_title)
50
+
51
+ # Cluster-buy signals (Business plan)
52
+ signals = client.signals.list(ticker="NVDA")
53
+ for sig in signals:
54
+ if sig.is_cluster_buy:
55
+ print(sig.company_name, sig.insider_count, "buyers")
56
+ ```
57
+
58
+ ## Async quickstart
59
+
60
+ ```python
61
+ import asyncio
62
+ from form4api import AsyncForm4ApiClient
63
+
64
+ async def main():
65
+ async with AsyncForm4ApiClient("YOUR_API_KEY") as client:
66
+ txns = await client.transactions.list(ticker="AAPL", per_page=5)
67
+ for t in txns:
68
+ print(t.insider_name, t.shares_amount, "@", t.price_per_share)
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ## Resources
74
+
75
+ | Resource | Methods |
76
+ |---|---|
77
+ | `client.transactions` | `.list(**params)`, `.paginate(**params)` |
78
+ | `client.insiders` | `.search(name, **params)`, `.get(cik)`, `.transactions(cik, **params)` |
79
+ | `client.companies` | `.get(ticker)`, `.insiders(ticker)` |
80
+ | `client.signals` | `.list(**params)` — Business plan |
81
+ | `client.webhooks` | `.create(url, event_types)`, `.list()`, `.delete(id)`, `.events(**params)` |
82
+
83
+ ## Error handling
84
+
85
+ ```python
86
+ from form4api import Form4ApiClient, AuthError, PlanError, RateLimitError, NotFoundError
87
+
88
+ client = Form4ApiClient("YOUR_API_KEY")
89
+
90
+ try:
91
+ signals = client.signals.list()
92
+ except PlanError as e:
93
+ print(f"Upgrade to {e.required_plan}")
94
+ except RateLimitError as e:
95
+ print(f"Retry after {e.retry_after}s")
96
+ except AuthError:
97
+ print("Invalid API key")
98
+ except NotFoundError:
99
+ print("Resource not found")
100
+ ```
101
+
102
+ ## License
103
+
104
+ MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "form4api"
7
- version = "0.1.0"
7
+ version = "0.3.0"
8
8
  description = "Python client for the Form4API — real-time SEC Form 4 insider trading data"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = ["httpx>=0.27"]
@@ -1,4 +1,4 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -23,11 +23,18 @@ TX = {
23
23
  "companyName": "Apple Inc.",
24
24
  "insiderName": "Cook Timothy D",
25
25
  "insiderCik": "0001214156",
26
+ "insiderTitle": "Chief Executive Officer",
27
+ "isDirector": False,
28
+ "isOfficer": True,
29
+ "is10PctOwner": False,
26
30
  "accessionNumber": "0001234567-26-000001",
27
31
  "securityTitle": "Common Stock",
28
32
  "transactionCode": "P",
33
+ "isOpenMarket": True,
34
+ "is10b5Plan": False,
29
35
  "sharesAmount": 1000.0,
30
36
  "pricePerShare": 212.45,
37
+ "totalValue": 212450.0,
31
38
  "sharesOwnedAfter": 5000.0,
32
39
  "directIndirect": "D",
33
40
  "isDerivative": False,
@@ -110,6 +117,18 @@ def test_transactions_paginate_stops_on_short_page(client):
110
117
 
111
118
  # ── insiders ──────────────────────────────────────────────────────────────────
112
119
 
120
+ @respx.mock
121
+ def test_insiders_search_returns_list(client):
122
+ route = respx.get(f"{BASE}/v1/insiders").mock(return_value=httpx.Response(200, json=[INSIDER]))
123
+ results = client.insiders.search("Cook")
124
+ assert route.called
125
+ qs = dict(route.calls[0].request.url.params)
126
+ assert qs["name"] == "Cook"
127
+ assert len(results) == 1
128
+ assert isinstance(results[0], Insider)
129
+ assert results[0].name == "Cook Timothy D"
130
+
131
+
113
132
  @respx.mock
114
133
  def test_insiders_get_returns_typed_object(client):
115
134
  respx.get(f"{BASE}/v1/insiders/0001214156").mock(return_value=httpx.Response(200, json=INSIDER))
@@ -1,29 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING
4
-
5
- from form4api._types import InsiderSignal
6
-
7
- if TYPE_CHECKING:
8
- from form4api._client import Form4ApiClient
9
-
10
-
11
- class SignalsResource:
12
- def __init__(self, client: Form4ApiClient) -> None:
13
- self._client = client
14
-
15
- def list(
16
- self,
17
- *,
18
- ticker: str | None = None,
19
- page: int = 1,
20
- per_page: int = 100,
21
- ) -> list[InsiderSignal]:
22
- params: dict[str, str] = {"page": str(page), "per_page": str(per_page)}
23
- if ticker is not None:
24
- params["ticker"] = ticker
25
- data = self._client._get("/v1/signals", params)
26
- return [InsiderSignal(**item) for item in data]
27
-
28
-
29
-
File without changes
File without changes
File without changes
File without changes