laplace-python-sdk 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 (33) hide show
  1. {laplace_python_sdk-0.1.0/src/laplace_python_sdk.egg-info → laplace_python_sdk-0.3.0}/PKG-INFO +107 -3
  2. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/README.md +106 -3
  3. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/pyproject.toml +4 -3
  4. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/src/laplace/__init__.py +2 -2
  5. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/src/laplace/base.py +31 -29
  6. laplace_python_sdk-0.3.0/src/laplace/brokers.py +214 -0
  7. laplace_python_sdk-0.3.0/src/laplace/capital_increase.py +92 -0
  8. laplace_python_sdk-0.3.0/src/laplace/client.py +69 -0
  9. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/src/laplace/collections.py +108 -78
  10. laplace_python_sdk-0.3.0/src/laplace/earnings.py +65 -0
  11. laplace_python_sdk-0.3.0/src/laplace/financials.py +91 -0
  12. laplace_python_sdk-0.3.0/src/laplace/funds.py +104 -0
  13. laplace_python_sdk-0.3.0/src/laplace/live_price.py +398 -0
  14. laplace_python_sdk-0.3.0/src/laplace/models.py +942 -0
  15. laplace_python_sdk-0.3.0/src/laplace/politician.py +59 -0
  16. laplace_python_sdk-0.3.0/src/laplace/search.py +51 -0
  17. laplace_python_sdk-0.3.0/src/laplace/state.py +107 -0
  18. laplace_python_sdk-0.3.0/src/laplace/stocks.py +345 -0
  19. laplace_python_sdk-0.3.0/src/laplace/websocket.py +514 -0
  20. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0/src/laplace_python_sdk.egg-info}/PKG-INFO +107 -3
  21. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/src/laplace_python_sdk.egg-info/SOURCES.txt +8 -1
  22. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/src/laplace_python_sdk.egg-info/requires.txt +1 -0
  23. laplace_python_sdk-0.1.0/src/laplace/client.py +0 -29
  24. laplace_python_sdk-0.1.0/src/laplace/financials.py +0 -15
  25. laplace_python_sdk-0.1.0/src/laplace/funds.py +0 -15
  26. laplace_python_sdk-0.1.0/src/laplace/li.py +0 -15
  27. laplace_python_sdk-0.1.0/src/laplace/models.py +0 -210
  28. laplace_python_sdk-0.1.0/src/laplace/stocks.py +0 -203
  29. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/LICENSE +0 -0
  30. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/MANIFEST.in +0 -0
  31. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/setup.cfg +0 -0
  32. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/src/laplace_python_sdk.egg-info/dependency_links.txt +0 -0
  33. {laplace_python_sdk-0.1.0 → laplace_python_sdk-0.3.0}/src/laplace_python_sdk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: laplace-python-sdk
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: Python SDK for Laplace stock data platform
5
5
  Author: Laplace SDK Team
6
6
  License: MIT
@@ -26,6 +26,7 @@ License-File: LICENSE
26
26
  Requires-Dist: httpx>=0.24.0
27
27
  Requires-Dist: pydantic>=2.0.0
28
28
  Requires-Dist: typing-extensions>=4.0.0; python_version < "3.10"
29
+ Requires-Dist: websocket-client>=1.8.0
29
30
  Provides-Extra: dev
30
31
  Requires-Dist: pytest>=7.0.0; extra == "dev"
31
32
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -51,6 +52,7 @@ The official Python SDK for the Laplace stock data platform. Get easy access to
51
52
  - 🧪 **Well-tested**: Comprehensive test coverage with real API integration
52
53
  - 🌍 **Multi-region**: Support for US and Turkish markets
53
54
  - ⚡ **Fast**: Built on httpx for high performance
55
+ - 🔌 **Real-time**: WebSocket support for live price data
54
56
 
55
57
  ## Installation
56
58
 
@@ -82,12 +84,22 @@ for collection in collections:
82
84
 
83
85
  # Get collection details
84
86
  collection_detail = client.collections.get_collection_detail(
85
- collection_id="620f455a0187ade00bb0d55f",
87
+ collection_id="620f455a0187ade00bb0d55f",
86
88
  region="tr"
87
89
  )
88
90
  print(f"Stocks in {collection_detail.title}:")
89
91
  for stock in collection_detail.stocks:
90
92
  print(f" {stock.symbol}: {stock.name}")
93
+
94
+ # Get politicians and their holdings
95
+ politicians = client.politicians.get_politicians()
96
+ for politician in politicians:
97
+ print(f"{politician.politician_name}: {politician.total_holdings} holdings")
98
+
99
+ # Get holdings for a specific stock
100
+ holdings = client.politicians.get_politician_holdings_by_symbol("AAPL")
101
+ for holding in holdings:
102
+ print(f"{holding.politician_name}: {holding.holding} ({holding.allocation})")
91
103
  ```
92
104
 
93
105
  ## API Reference
@@ -113,7 +125,7 @@ from laplace.stocks import HistoricalPriceInterval
113
125
 
114
126
  prices = client.stocks.get_price_with_interval(
115
127
  symbol="AAPL",
116
- region="us",
128
+ region="us",
117
129
  from_date=datetime(2024, 1, 1),
118
130
  to_date=datetime(2024, 1, 31),
119
131
  interval=HistoricalPriceInterval.ONE_DAY
@@ -154,6 +166,97 @@ sectors = client.collections.get_sectors(region="tr", locale="en")
154
166
  sector_detail = client.collections.get_sector_detail(sector_id="id", region="tr")
155
167
  ```
156
168
 
169
+ ### Politicians Client
170
+
171
+ ```python
172
+ # Get all politicians
173
+ politicians = client.politicians.get_politicians()
174
+
175
+ # Get politician detail
176
+ politician = client.politicians.get_politician_detail(id=1)
177
+
178
+ # Get holdings by stock symbol
179
+ holdings = client.politicians.get_politician_holdings_by_symbol(symbol="AAPL")
180
+
181
+ # Get top holdings
182
+ top_holdings = client.politicians.get_top_holdings()
183
+ ```
184
+
185
+ ### WebSocket Client (Live Price Data)
186
+
187
+ ```python
188
+ import asyncio
189
+ from laplace import LaplaceClient, LivePriceFeed, WebSocketOptions, LogLevel
190
+
191
+ # Initialize the client
192
+ client = LaplaceClient(api_key="your-api-key")
193
+
194
+ # Create WebSocket options
195
+ options = WebSocketOptions(
196
+ enable_logging=True,
197
+ log_level=LogLevel.INFO,
198
+ reconnect_attempts=5,
199
+ reconnect_delay=5000,
200
+ max_reconnect_delay=30000,
201
+ )
202
+
203
+ # Create WebSocket client
204
+ websocket_client = client.create_websocket_client(
205
+ feeds=[LivePriceFeed.LIVE_BIST, LivePriceFeed.LIVE_US],
206
+ external_user_id="your-user-id",
207
+ options=options,
208
+ )
209
+
210
+ # Define callback function for price updates
211
+ def on_price_update(data):
212
+ print(f"Price update for {data.symbol}: {data.close_price}")
213
+ if hasattr(data, 'percent_change'):
214
+ print(f" Change: {data.percent_change}%")
215
+ print(f" Timestamp: {data.timestamp}")
216
+
217
+ async def main():
218
+ # Connect to WebSocket
219
+ await websocket_client.connect()
220
+
221
+ # Subscribe to symbols
222
+ symbols_bist = ["THYAO", "GARAN", "AKBNK"]
223
+ symbols_us = ["AAPL", "GOOGL", "MSFT"]
224
+
225
+ # Subscribe to BIST symbols
226
+ unsubscribe_bist = websocket_client.subscribe(
227
+ symbols=symbols_bist,
228
+ feed=LivePriceFeed.LIVE_BIST,
229
+ handler=on_price_update
230
+ )
231
+
232
+ # Subscribe to US symbols
233
+ unsubscribe_us = websocket_client.subscribe(
234
+ symbols=symbols_us,
235
+ feed=LivePriceFeed.LIVE_US,
236
+ handler=on_price_update
237
+ )
238
+
239
+ # Keep connection alive
240
+ try:
241
+ while True:
242
+ await asyncio.sleep(1)
243
+ except KeyboardInterrupt:
244
+ # Cleanup
245
+ unsubscribe_bist()
246
+ unsubscribe_us()
247
+ await websocket_client.close()
248
+
249
+ # Run the WebSocket client
250
+ asyncio.run(main())
251
+ ```
252
+
253
+ ### Available WebSocket Feeds
254
+
255
+ - `LivePriceFeed.LIVE_BIST`: Live BIST (Turkish) stock prices
256
+ - `LivePriceFeed.LIVE_US`: Live US stock prices
257
+ - `LivePriceFeed.DELAYED_BIST`: Delayed BIST stock prices
258
+ - `LivePriceFeed.DEPTH_TR`: Depth data for TR (commented out, available if needed)
259
+
157
260
  ## Supported Regions
158
261
 
159
262
  - **US**: United States stock market
@@ -212,6 +315,7 @@ LAPLACE_API_KEY=your-key pytest -m integration
212
315
  - Python 3.8+
213
316
  - httpx >= 0.24.0
214
317
  - pydantic >= 2.0.0
318
+ - websocket-client >= 1.8.0
215
319
 
216
320
  ## Documentation
217
321
 
@@ -14,6 +14,7 @@ The official Python SDK for the Laplace stock data platform. Get easy access to
14
14
  - 🧪 **Well-tested**: Comprehensive test coverage with real API integration
15
15
  - 🌍 **Multi-region**: Support for US and Turkish markets
16
16
  - ⚡ **Fast**: Built on httpx for high performance
17
+ - 🔌 **Real-time**: WebSocket support for live price data
17
18
 
18
19
  ## Installation
19
20
 
@@ -45,12 +46,22 @@ for collection in collections:
45
46
 
46
47
  # Get collection details
47
48
  collection_detail = client.collections.get_collection_detail(
48
- collection_id="620f455a0187ade00bb0d55f",
49
+ collection_id="620f455a0187ade00bb0d55f",
49
50
  region="tr"
50
51
  )
51
52
  print(f"Stocks in {collection_detail.title}:")
52
53
  for stock in collection_detail.stocks:
53
54
  print(f" {stock.symbol}: {stock.name}")
55
+
56
+ # Get politicians and their holdings
57
+ politicians = client.politicians.get_politicians()
58
+ for politician in politicians:
59
+ print(f"{politician.politician_name}: {politician.total_holdings} holdings")
60
+
61
+ # Get holdings for a specific stock
62
+ holdings = client.politicians.get_politician_holdings_by_symbol("AAPL")
63
+ for holding in holdings:
64
+ print(f"{holding.politician_name}: {holding.holding} ({holding.allocation})")
54
65
  ```
55
66
 
56
67
  ## API Reference
@@ -76,7 +87,7 @@ from laplace.stocks import HistoricalPriceInterval
76
87
 
77
88
  prices = client.stocks.get_price_with_interval(
78
89
  symbol="AAPL",
79
- region="us",
90
+ region="us",
80
91
  from_date=datetime(2024, 1, 1),
81
92
  to_date=datetime(2024, 1, 31),
82
93
  interval=HistoricalPriceInterval.ONE_DAY
@@ -117,6 +128,97 @@ sectors = client.collections.get_sectors(region="tr", locale="en")
117
128
  sector_detail = client.collections.get_sector_detail(sector_id="id", region="tr")
118
129
  ```
119
130
 
131
+ ### Politicians Client
132
+
133
+ ```python
134
+ # Get all politicians
135
+ politicians = client.politicians.get_politicians()
136
+
137
+ # Get politician detail
138
+ politician = client.politicians.get_politician_detail(id=1)
139
+
140
+ # Get holdings by stock symbol
141
+ holdings = client.politicians.get_politician_holdings_by_symbol(symbol="AAPL")
142
+
143
+ # Get top holdings
144
+ top_holdings = client.politicians.get_top_holdings()
145
+ ```
146
+
147
+ ### WebSocket Client (Live Price Data)
148
+
149
+ ```python
150
+ import asyncio
151
+ from laplace import LaplaceClient, LivePriceFeed, WebSocketOptions, LogLevel
152
+
153
+ # Initialize the client
154
+ client = LaplaceClient(api_key="your-api-key")
155
+
156
+ # Create WebSocket options
157
+ options = WebSocketOptions(
158
+ enable_logging=True,
159
+ log_level=LogLevel.INFO,
160
+ reconnect_attempts=5,
161
+ reconnect_delay=5000,
162
+ max_reconnect_delay=30000,
163
+ )
164
+
165
+ # Create WebSocket client
166
+ websocket_client = client.create_websocket_client(
167
+ feeds=[LivePriceFeed.LIVE_BIST, LivePriceFeed.LIVE_US],
168
+ external_user_id="your-user-id",
169
+ options=options,
170
+ )
171
+
172
+ # Define callback function for price updates
173
+ def on_price_update(data):
174
+ print(f"Price update for {data.symbol}: {data.close_price}")
175
+ if hasattr(data, 'percent_change'):
176
+ print(f" Change: {data.percent_change}%")
177
+ print(f" Timestamp: {data.timestamp}")
178
+
179
+ async def main():
180
+ # Connect to WebSocket
181
+ await websocket_client.connect()
182
+
183
+ # Subscribe to symbols
184
+ symbols_bist = ["THYAO", "GARAN", "AKBNK"]
185
+ symbols_us = ["AAPL", "GOOGL", "MSFT"]
186
+
187
+ # Subscribe to BIST symbols
188
+ unsubscribe_bist = websocket_client.subscribe(
189
+ symbols=symbols_bist,
190
+ feed=LivePriceFeed.LIVE_BIST,
191
+ handler=on_price_update
192
+ )
193
+
194
+ # Subscribe to US symbols
195
+ unsubscribe_us = websocket_client.subscribe(
196
+ symbols=symbols_us,
197
+ feed=LivePriceFeed.LIVE_US,
198
+ handler=on_price_update
199
+ )
200
+
201
+ # Keep connection alive
202
+ try:
203
+ while True:
204
+ await asyncio.sleep(1)
205
+ except KeyboardInterrupt:
206
+ # Cleanup
207
+ unsubscribe_bist()
208
+ unsubscribe_us()
209
+ await websocket_client.close()
210
+
211
+ # Run the WebSocket client
212
+ asyncio.run(main())
213
+ ```
214
+
215
+ ### Available WebSocket Feeds
216
+
217
+ - `LivePriceFeed.LIVE_BIST`: Live BIST (Turkish) stock prices
218
+ - `LivePriceFeed.LIVE_US`: Live US stock prices
219
+ - `LivePriceFeed.DELAYED_BIST`: Delayed BIST stock prices
220
+ - `LivePriceFeed.DEPTH_TR`: Depth data for TR (commented out, available if needed)
221
+
120
222
  ## Supported Regions
121
223
 
122
224
  - **US**: United States stock market
@@ -175,6 +277,7 @@ LAPLACE_API_KEY=your-key pytest -m integration
175
277
  - Python 3.8+
176
278
  - httpx >= 0.24.0
177
279
  - pydantic >= 2.0.0
280
+ - websocket-client >= 1.8.0
178
281
 
179
282
  ## Documentation
180
283
 
@@ -182,4 +285,4 @@ Full API documentation is available at [laplace.finfree.co/en/docs](https://lapl
182
285
 
183
286
  ## License
184
287
 
185
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
288
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "laplace-python-sdk"
7
- version = "0.1.0"
7
+ version = "0.3.0"
8
8
  description = "Python SDK for Laplace stock data platform"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -30,6 +30,7 @@ dependencies = [
30
30
  "httpx>=0.24.0",
31
31
  "pydantic>=2.0.0",
32
32
  "typing-extensions>=4.0.0; python_version<'3.10'",
33
+ "websocket-client>=1.8.0",
33
34
  ]
34
35
 
35
36
  [project.optional-dependencies]
@@ -63,14 +64,14 @@ profile = "black"
63
64
  line_length = 100
64
65
 
65
66
  [tool.mypy]
66
- python_version = "0.1.0"
67
+ python_version = "0.3.0"
67
68
  strict = true
68
69
  warn_return_any = true
69
70
  warn_unused_configs = true
70
71
 
71
72
  [tool.ruff]
72
73
  line-length = 100
73
- target-version = "0.1.0"
74
+ target-version = "0.3.0"
74
75
  select = ["E", "W", "F", "I", "N", "B", "UP"]
75
76
  ignore = ["B008", "N805"]
76
77
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  from .client import LaplaceClient
4
4
 
5
- __version__ = "0.1.0"
6
- __all__ = ["LaplaceClient"]
5
+ __version__ = "0.1.1"
6
+ __all__ = ["LaplaceClient"]
@@ -1,19 +1,25 @@
1
1
  """Base client for Laplace API."""
2
2
 
3
- from typing import Any, Dict, Optional, Union
3
+ from typing import Any, Dict, Optional
4
+
4
5
  import httpx
5
- from pydantic import BaseModel
6
6
 
7
7
 
8
8
  class LaplaceError(Exception):
9
9
  """Base exception for Laplace API errors."""
10
+
10
11
  pass
11
12
 
12
13
 
13
14
  class LaplaceAPIError(LaplaceError):
14
15
  """Exception raised for API errors."""
15
-
16
- def __init__(self, message: str, status_code: Optional[int] = None, response: Optional[Dict[str, Any]] = None):
16
+
17
+ def __init__(
18
+ self,
19
+ message: str,
20
+ status_code: Optional[int] = None,
21
+ response: Optional[Dict[str, Any]] = None,
22
+ ):
17
23
  super().__init__(message)
18
24
  self.status_code = status_code
19
25
  self.response = response
@@ -21,10 +27,10 @@ class LaplaceAPIError(LaplaceError):
21
27
 
22
28
  class BaseClient:
23
29
  """Base client for Laplace API communication."""
24
-
30
+
25
31
  def __init__(self, api_key: str, base_url: str = "https://api.finfree.app/api"):
26
32
  """Initialize the base client.
27
-
33
+
28
34
  Args:
29
35
  api_key: Your Laplace API key
30
36
  base_url: Base URL for the API (default: https://api.finfree.app/api)
@@ -37,83 +43,79 @@ class BaseClient:
37
43
  },
38
44
  timeout=30.0,
39
45
  )
40
-
46
+
41
47
  def __enter__(self):
42
48
  return self
43
-
49
+
44
50
  def __exit__(self, exc_type, exc_val, exc_tb):
45
51
  self.close()
46
-
52
+
47
53
  def close(self):
48
54
  """Close the HTTP client."""
49
55
  self._client.close()
50
-
56
+
51
57
  def _request(
52
58
  self,
53
59
  method: str,
54
60
  endpoint: str,
55
61
  params: Optional[Dict[str, Any]] = None,
56
62
  json: Optional[Dict[str, Any]] = None,
57
- **kwargs
63
+ **kwargs,
58
64
  ) -> Dict[str, Any]:
59
65
  """Make a request to the API.
60
-
66
+
61
67
  Args:
62
68
  method: HTTP method
63
69
  endpoint: API endpoint (relative to base_url)
64
70
  params: Query parameters
65
71
  json: JSON body data
66
72
  **kwargs: Additional arguments passed to httpx
67
-
73
+
68
74
  Returns:
69
75
  Response data as dictionary
70
-
76
+
71
77
  Raises:
72
78
  LaplaceAPIError: If the API request fails
73
79
  """
74
80
  url = f"{self.base_url}/{endpoint.lstrip('/')}"
75
-
81
+
76
82
  # Add API key to params
77
83
  if params is None:
78
84
  params = {}
79
85
  params["api_key"] = self.api_key
80
-
86
+
81
87
  try:
82
88
  response = self._client.request(
83
- method=method,
84
- url=url,
85
- params=params,
86
- json=json,
87
- **kwargs
89
+ method=method, url=url, params=params, json=json, **kwargs
88
90
  )
89
91
  response.raise_for_status()
90
92
  return response.json()
91
93
  except httpx.HTTPStatusError as e:
92
94
  try:
93
95
  error_data = e.response.json()
94
- except:
96
+ except Exception:
95
97
  error_data = {"error": e.response.text}
96
-
98
+
97
99
  raise LaplaceAPIError(
98
100
  message=f"API request failed: {e.response.status_code} {e.response.reason_phrase}",
99
101
  status_code=e.response.status_code,
100
- response=error_data
102
+ response=error_data,
101
103
  ) from e
102
104
  except httpx.RequestError as e:
103
105
  raise LaplaceAPIError(f"Request failed: {str(e)}") from e
104
-
106
+
105
107
  def get(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
106
108
  """Make a GET request."""
107
109
  return self._request("GET", endpoint, params=params)
108
-
110
+
109
111
  def post(self, endpoint: str, json: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
110
112
  """Make a POST request."""
111
113
  return self._request("POST", endpoint, json=json)
112
-
114
+
113
115
  def put(self, endpoint: str, json: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
114
116
  """Make a PUT request."""
115
117
  return self._request("PUT", endpoint, json=json)
116
-
118
+
117
119
  def delete(self, endpoint: str) -> Dict[str, Any]:
118
120
  """Make a DELETE request."""
119
- return self._request("DELETE", endpoint)
121
+ return self._request("DELETE", endpoint)