laplace-python-sdk 0.4.0__tar.gz → 1.1.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 (28) hide show
  1. {laplace_python_sdk-0.4.0/src/laplace_python_sdk.egg-info → laplace_python_sdk-1.1.0}/PKG-INFO +1 -1
  2. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/pyproject.toml +3 -3
  3. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/base.py +31 -1
  4. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/brokers.py +24 -19
  5. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/capital_increase.py +8 -7
  6. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/client.py +3 -1
  7. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/collections.py +132 -29
  8. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/financials.py +6 -3
  9. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/live_price.py +85 -7
  10. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/models.py +189 -204
  11. laplace_python_sdk-1.1.0/src/laplace/news.py +94 -0
  12. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/search.py +9 -1
  13. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/stocks.py +61 -13
  14. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/websocket.py +20 -88
  15. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0/src/laplace_python_sdk.egg-info}/PKG-INFO +1 -1
  16. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace_python_sdk.egg-info/SOURCES.txt +1 -0
  17. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/LICENSE +0 -0
  18. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/MANIFEST.in +0 -0
  19. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/README.md +0 -0
  20. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/setup.cfg +0 -0
  21. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/__init__.py +0 -0
  22. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/earnings.py +0 -0
  23. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/funds.py +0 -0
  24. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/politician.py +0 -0
  25. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace/state.py +0 -0
  26. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace_python_sdk.egg-info/dependency_links.txt +0 -0
  27. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.0}/src/laplace_python_sdk.egg-info/requires.txt +0 -0
  28. {laplace_python_sdk-0.4.0 → laplace_python_sdk-1.1.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.4.0
3
+ Version: 1.1.0
4
4
  Summary: Python SDK for Laplace stock data platform
5
5
  Author: Laplace SDK Team
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "laplace-python-sdk"
7
- version = "0.4.0"
7
+ version = "1.1.0"
8
8
  description = "Python SDK for Laplace stock data platform"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -64,14 +64,14 @@ profile = "black"
64
64
  line_length = 100
65
65
 
66
66
  [tool.mypy]
67
- python_version = "0.4.0"
67
+ python_version = "1.1.0"
68
68
  strict = true
69
69
  warn_return_any = true
70
70
  warn_unused_configs = true
71
71
 
72
72
  [tool.ruff]
73
73
  line-length = 100
74
- target-version = "0.4.0"
74
+ target-version = "1.1.0"
75
75
  select = ["E", "W", "F", "I", "N", "B", "UP"]
76
76
  ignore = ["B008", "N805"]
77
77
 
@@ -39,7 +39,7 @@ class BaseClient:
39
39
  self.base_url = base_url.rstrip("/")
40
40
  self._client = httpx.Client(
41
41
  headers={
42
- "User-Agent": "laplace-python-sdk/0.1.0",
42
+ "User-Agent": "laplace-python-sdk/1.0.0",
43
43
  },
44
44
  timeout=30.0,
45
45
  )
@@ -119,3 +119,33 @@ class BaseClient:
119
119
  def delete(self, endpoint: str) -> Dict[str, Any]:
120
120
  """Make a DELETE request."""
121
121
  return self._request("DELETE", endpoint)
122
+
123
+ def patch(self, endpoint: str, json: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
124
+ """Make a PATCH request."""
125
+ return self._request("PATCH", endpoint, json=json)
126
+
127
+ def get_bytes(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> bytes:
128
+ """Make a GET request and return raw bytes."""
129
+ url = f"{self.base_url}/{endpoint.lstrip('/')}"
130
+
131
+ if params is None:
132
+ params = {}
133
+ params["api_key"] = self.api_key
134
+
135
+ try:
136
+ response = self._client.request(method="GET", url=url, params=params)
137
+ response.raise_for_status()
138
+ return response.content
139
+ except httpx.HTTPStatusError as e:
140
+ try:
141
+ error_data = e.response.json()
142
+ except Exception:
143
+ error_data = {"error": e.response.text}
144
+
145
+ raise LaplaceAPIError(
146
+ message=f"API request failed: {e.response.status_code} {e.response.reason_phrase}",
147
+ status_code=e.response.status_code,
148
+ response=error_data,
149
+ ) from e
150
+ except httpx.RequestError as e:
151
+ raise LaplaceAPIError(f"Request failed: {str(e)}") from e
@@ -1,16 +1,17 @@
1
1
  """Brokers client for Laplace API."""
2
2
 
3
3
  from datetime import datetime
4
+ from typing import Optional
4
5
 
5
6
  from laplace.base import BaseClient
6
7
 
7
8
  from .models import (
9
+ AssetClass,
8
10
  Broker,
11
+ BrokerList,
9
12
  PaginationPageSize,
10
13
  Region,
11
- BrokerMarketData,
12
- BrokerStockData,
13
- BrokerSortDirection,
14
+ SortDirection,
14
15
  BrokerSort,
15
16
  PaginatedResponse,
16
17
  )
@@ -32,6 +33,7 @@ class BrokersClient:
32
33
  region: Region = Region.TR,
33
34
  page: int = 0,
34
35
  size: PaginationPageSize = PaginationPageSize.PAGE_SIZE_10,
36
+ asset_class: Optional[AssetClass] = None
35
37
  ) -> PaginatedResponse[Broker]:
36
38
  """Retrieve all brokers.
37
39
 
@@ -47,6 +49,9 @@ class BrokersClient:
47
49
 
48
50
  params = {"region": region.value, "page": page, "size": size.value}
49
51
 
52
+ if asset_class:
53
+ params["assetClass"] = asset_class.value
54
+
50
55
  response = self._client.get("v1/brokers", params=params)
51
56
  return PaginatedResponse[Broker](**response)
52
57
 
@@ -55,12 +60,12 @@ class BrokersClient:
55
60
  symbol: str,
56
61
  region: Region,
57
62
  sort_by: BrokerSort,
58
- sort_direction: BrokerSortDirection,
63
+ sort_direction: SortDirection,
59
64
  from_date: datetime,
60
65
  to_date: datetime,
61
66
  page: int = 0,
62
67
  size: PaginationPageSize = PaginationPageSize.PAGE_SIZE_10,
63
- ) -> BrokerStockData:
68
+ ) -> BrokerList:
64
69
  """Retrieve broker information by symbol.
65
70
 
66
71
  Args:
@@ -73,7 +78,7 @@ class BrokersClient:
73
78
  page: Page number (default: 0)
74
79
  size: Page size (default: 10)
75
80
  Returns:
76
- BrokerStockData: Broker information
81
+ BrokerList: Broker information
77
82
  """
78
83
  if region != Region.TR:
79
84
  raise ValueError("Broker endpoint only works with the 'tr' region")
@@ -89,18 +94,18 @@ class BrokersClient:
89
94
  }
90
95
 
91
96
  response = self._client.get(f"v1/brokers/stock/{symbol}", params=params)
92
- return BrokerStockData(**response)
97
+ return BrokerList(**response)
93
98
 
94
99
  def get_broker_list_for_market(
95
100
  self,
96
101
  region: Region,
97
102
  sort_by: BrokerSort,
98
- sort_direction: BrokerSortDirection,
103
+ sort_direction: SortDirection,
99
104
  from_date: datetime,
100
105
  to_date: datetime,
101
106
  page: int = 0,
102
107
  size: PaginationPageSize = PaginationPageSize.PAGE_SIZE_10,
103
- ) -> BrokerMarketData:
108
+ ) -> BrokerList:
104
109
  """Retrieve broker market data.
105
110
 
106
111
  Args:
@@ -113,7 +118,7 @@ class BrokersClient:
113
118
  size: Page size (default: 10)
114
119
 
115
120
  Returns:
116
- BrokerStockData: Broker market data
121
+ BrokerList: Broker market data
117
122
  """
118
123
  if region != Region.TR:
119
124
  raise ValueError("Broker market endpoint only works with the 'tr' region")
@@ -129,19 +134,19 @@ class BrokersClient:
129
134
  }
130
135
 
131
136
  response = self._client.get("v1/brokers/market", params=params)
132
- return BrokerMarketData(**response)
137
+ return BrokerList(**response)
133
138
 
134
139
  def get_broker_list_for_stock(
135
140
  self,
136
141
  symbol: str,
137
142
  region: Region,
138
143
  sort_by: BrokerSort,
139
- sort_direction: BrokerSortDirection,
144
+ sort_direction: SortDirection,
140
145
  from_date: datetime,
141
146
  to_date: datetime,
142
147
  page: int = 0,
143
148
  size: PaginationPageSize = PaginationPageSize.PAGE_SIZE_10,
144
- ) -> BrokerMarketData:
149
+ ) -> BrokerList:
145
150
  """Retrieve stock data for a specific broker.
146
151
 
147
152
  Args:
@@ -155,7 +160,7 @@ class BrokersClient:
155
160
  size: Page size (default: 10)
156
161
 
157
162
  Returns:
158
- BrokerStockData: Stock data for the broker
163
+ BrokerList: Stock data for the broker
159
164
  """
160
165
  if region != Region.TR:
161
166
  raise ValueError("Broker stock endpoint only works with the 'tr' region")
@@ -171,18 +176,18 @@ class BrokersClient:
171
176
  }
172
177
 
173
178
  response = self._client.get(f"v1/brokers/{symbol}", params=params)
174
- return BrokerMarketData(**response)
179
+ return BrokerList(**response)
175
180
 
176
181
  def get_stock_list_for_market(
177
182
  self,
178
183
  region: Region,
179
184
  sort_by: BrokerSort,
180
- sort_direction: BrokerSortDirection,
185
+ sort_direction: SortDirection,
181
186
  from_date: datetime,
182
187
  to_date: datetime,
183
188
  page: int = 0,
184
189
  size: PaginationPageSize = PaginationPageSize.PAGE_SIZE_10,
185
- ) -> BrokerStockData:
190
+ ) -> BrokerList:
186
191
  """Retrieve market stock data for brokers.
187
192
 
188
193
  Args:
@@ -195,7 +200,7 @@ class BrokersClient:
195
200
  size: Page size (default: 10)
196
201
 
197
202
  Returns:
198
- BrokerStockData: Market stock data for brokers
203
+ BrokerList: Market stock data for brokers
199
204
  """
200
205
  if region != Region.TR:
201
206
  raise ValueError("Broker market stock endpoint only works with the 'tr' region")
@@ -211,4 +216,4 @@ class BrokersClient:
211
216
  }
212
217
 
213
218
  response = self._client.get("v1/brokers/market/stock", params=params)
214
- return BrokerStockData(**response)
219
+ return BrokerList(**response)
@@ -1,6 +1,7 @@
1
1
  """Capital Increase client for Laplace API."""
2
2
 
3
- from typing import List
3
+ from datetime import datetime
4
+ from typing import List, Optional
4
5
  from laplace.base import BaseClient
5
6
 
6
7
  from .models import (
@@ -53,7 +54,7 @@ class CapitalIncreaseClient:
53
54
  page: int = 0,
54
55
  size: PaginationPageSize = PaginationPageSize.PAGE_SIZE_10,
55
56
  ) -> PaginatedResponse[CapitalIncrease]:
56
- """Retrieve capital increase information by ID.
57
+ """Retrieve capital increase information by symbol.
57
58
 
58
59
  Args:
59
60
  symbol: Stock symbol (e.g., "AKBNK")
@@ -72,20 +73,20 @@ class CapitalIncreaseClient:
72
73
  response = self._client.get(f"v1/capital-increase/{symbol}", params=params)
73
74
  return PaginatedResponse[CapitalIncrease](**response)
74
75
 
75
- def get_active_rights(self, symbol: str, region: Region = Region.TR) -> List[CapitalIncrease]:
76
+ def get_active_rights(self, symbol: str, date: Optional[datetime] = None) -> List[CapitalIncrease]:
76
77
  """Retrieve active rights for a specific stock.
77
78
 
78
79
  Args:
79
80
  symbol: Stock symbol (e.g., "AKBNK")
80
- region: Region code (only 'tr' is supported) (default: tr)
81
+ date: Optional date filter (defaults to today on the server)
81
82
 
82
83
  Returns:
83
84
  List[CapitalIncrease]: Active rights data
84
85
  """
85
- if region != Region.TR:
86
- raise ValueError("Rights endpoint only works with the 'tr' region")
86
+ params = {}
87
87
 
88
- params = {"region": region.value}
88
+ if date is not None:
89
+ params["date"] = date.strftime("%Y-%m-%d")
89
90
 
90
91
  response = self._client.get(f"v1/rights/active/{symbol}", params=params)
91
92
 
@@ -1,5 +1,6 @@
1
1
  """Main Laplace client."""
2
2
 
3
+ from laplace.news import NewsClient
3
4
  from .base import BaseClient
4
5
  from .brokers import BrokersClient
5
6
  from .capital_increase import CapitalIncreaseClient
@@ -19,7 +20,7 @@ from typing import Optional, List
19
20
  class LaplaceClient(BaseClient):
20
21
  """Main Laplace API client with all sub-clients."""
21
22
 
22
- def __init__(self, api_key: str, base_url: str = "https://uat.api.finfree.app/api"):
23
+ def __init__(self, api_key: str, base_url: str = "https://api.finfree.app/api"):
23
24
  """Initialize the Laplace client.
24
25
 
25
26
  Args:
@@ -40,6 +41,7 @@ class LaplaceClient(BaseClient):
40
41
  self.earnings = EarningsClient(self)
41
42
  self.search = SearchClient(self)
42
43
  self.state = StateClient(self)
44
+ self.news = NewsClient(self)
43
45
 
44
46
  # WebSocket client will be created on demand
45
47
  self._websocket_client: Optional[LivePriceWebSocketClient] = None
@@ -1,20 +1,15 @@
1
1
  """Collections client for Laplace API."""
2
2
 
3
- from typing import List, Optional
3
+ from typing import Dict, List, Optional
4
4
 
5
5
  from laplace.base import BaseClient
6
6
 
7
7
  from .models import (
8
8
  Collection,
9
9
  CollectionDetail,
10
- Industry,
11
- IndustryDetail,
10
+ CollectionStatus,
12
11
  Locale,
13
- Region,
14
- Sector,
15
- SectorDetail,
16
- Theme,
17
- ThemeDetail,
12
+ Region
18
13
  )
19
14
 
20
15
 
@@ -62,7 +57,7 @@ class CollectionsClient:
62
57
  response = self._client.get(f"v1/collection/{collection_id}", params=params)
63
58
  return CollectionDetail(**response)
64
59
 
65
- def get_themes(self, region: Region, locale: Locale = "en") -> List[Theme]:
60
+ def get_themes(self, region: Region, locale: Locale = "en") -> List[Collection]:
66
61
  """Retrieve a list of themes along with the number of stocks in each.
67
62
 
68
63
  Args:
@@ -70,14 +65,14 @@ class CollectionsClient:
70
65
  locale: Locale code (tr, en) (default: en)
71
66
 
72
67
  Returns:
73
- List[Theme]: List of themes
68
+ List[Collection]: List of themes
74
69
  """
75
70
  params = {"region": region.value, "locale": locale}
76
71
 
77
72
  response = self._client.get("v1/theme", params=params)
78
- return [Theme(**theme) for theme in response]
73
+ return [Collection(**theme) for theme in response]
79
74
 
80
- def get_theme_detail(self, theme_id: str, region: Region, locale: Locale = "en") -> ThemeDetail:
75
+ def get_theme_detail(self, theme_id: str, region: Region, locale: Locale = "en") -> CollectionDetail:
81
76
  """Retrieve detailed information about a specific theme.
82
77
 
83
78
  Args:
@@ -86,14 +81,14 @@ class CollectionsClient:
86
81
  locale: Locale code (tr, en) (default: en)
87
82
 
88
83
  Returns:
89
- ThemeDetail: Detailed theme information
84
+ CollectionDetail: Detailed theme information
90
85
  """
91
86
  params = {"locale": locale, "region": region.value}
92
87
 
93
88
  response = self._client.get(f"v1/theme/{theme_id}", params=params)
94
- return ThemeDetail(**response)
89
+ return CollectionDetail(**response)
95
90
 
96
- def get_industries(self, region: Region, locale: Locale = "en") -> List[Industry]:
91
+ def get_industries(self, region: Region, locale: Locale = "en") -> List[Collection]:
97
92
  """Retrieve a list of industries along with the number of stocks in each.
98
93
 
99
94
  Args:
@@ -101,16 +96,16 @@ class CollectionsClient:
101
96
  locale: Locale code (tr, en) (default: en)
102
97
 
103
98
  Returns:
104
- List[Industry]: List of industries
99
+ List[Collection]: List of industries
105
100
  """
106
101
  params = {"region": region.value, "locale": locale}
107
102
 
108
103
  response = self._client.get("v1/industry", params=params)
109
- return [Industry(**industry) for industry in response]
104
+ return [Collection(**industry) for industry in response]
110
105
 
111
106
  def get_industry_detail(
112
107
  self, industry_id: str, region: Region, locale: Locale = "en"
113
- ) -> IndustryDetail:
108
+ ) -> CollectionDetail:
114
109
  """Retrieve detailed information about a specific industry.
115
110
 
116
111
  Args:
@@ -119,14 +114,14 @@ class CollectionsClient:
119
114
  locale: Locale code (tr, en) (default: en)
120
115
 
121
116
  Returns:
122
- IndustryDetail: Detailed industry information
117
+ CollectionDetail: Detailed industry information
123
118
  """
124
119
  params = {"locale": locale, "region": region.value}
125
120
 
126
121
  response = self._client.get(f"v1/industry/{industry_id}", params=params)
127
- return IndustryDetail(**response)
122
+ return CollectionDetail(**response)
128
123
 
129
- def get_custom_themes(self, locale: Locale, region: Region) -> List[CollectionDetail]:
124
+ def get_custom_themes(self, locale: Locale, region: Region) -> List[Collection]:
130
125
  """Get a list of all your custom themes.
131
126
 
132
127
  Args:
@@ -134,12 +129,12 @@ class CollectionsClient:
134
129
  region: Region code (tr, us)
135
130
 
136
131
  Returns:
137
- List[CollectionDetail]: List of custom themes
132
+ List[Collection]: List of custom themes
138
133
  """
139
134
  params = {"locale": locale, "region": region.value}
140
135
 
141
136
  response = self._client.get("v1/custom-theme", params=params)
142
- return [CollectionDetail(**theme) for theme in response]
137
+ return [Collection(**theme) for theme in response]
143
138
 
144
139
  def get_custom_theme_detail(
145
140
  self, theme_id: str, locale: Locale, region: Region, sort_by: Optional[str] = None
@@ -163,7 +158,115 @@ class CollectionsClient:
163
158
  response = self._client.get(f"v1/custom-theme/{theme_id}", params=params)
164
159
  return CollectionDetail(**response)
165
160
 
166
- def get_sectors(self, region: Region, locale: Locale = "en") -> List[Sector]:
161
+ def delete_custom_theme(self, theme_id: str) -> None:
162
+ """Delete specific custom theme.
163
+
164
+ Args:
165
+ theme_id: Unique identifier for the custom theme
166
+ """
167
+ self._client.delete(f"v1/custom-theme/{theme_id}")
168
+
169
+ def create_custom_theme(
170
+ self,
171
+ title: Dict[str, str],
172
+ stock_ids: List[str],
173
+ status: CollectionStatus,
174
+ description: Optional[Dict[str, str]] = None,
175
+ region: Optional[List[Region]] = None,
176
+ image_url: Optional[str] = None,
177
+ image: Optional[str] = None,
178
+ avatar_url: Optional[str] = None,
179
+ order: Optional[int] = None,
180
+ meta_data: Optional[dict] = None,
181
+ ) -> str:
182
+ """Create a new custom theme.
183
+
184
+ Args:
185
+ title: Localized title (e.g., {"tr": "Başlık", "en": "Title"})
186
+ stock_ids: List of stock IDs
187
+ status: Collection status
188
+ description: Localized description (optional)
189
+ region: List of regions (optional)
190
+ image_url: Image URL (optional)
191
+ image: Image data (optional)
192
+ avatar_url: Avatar URL (optional)
193
+ order: Display order (optional)
194
+ meta_data: Additional metadata (optional)
195
+
196
+ Returns:
197
+ str: Created theme ID
198
+ """
199
+ body: dict = {
200
+ "title": title,
201
+ "stocks": stock_ids,
202
+ "status": status.value,
203
+ }
204
+
205
+ if description is not None:
206
+ body["description"] = description
207
+ if region is not None:
208
+ body["region"] = [r.value for r in region]
209
+ if image_url is not None:
210
+ body["image_url"] = image_url
211
+ if image is not None:
212
+ body["image"] = image
213
+ if avatar_url is not None:
214
+ body["avatar_url"] = avatar_url
215
+ if order is not None:
216
+ body["order"] = order
217
+ if meta_data is not None:
218
+ body["meta_data"] = meta_data
219
+
220
+ response = self._client.post("v1/custom-theme", json=body)
221
+ return response["id"]
222
+
223
+ def update_custom_theme(
224
+ self,
225
+ theme_id: str,
226
+ title: Optional[Dict[str, str]] = None,
227
+ stock_ids: Optional[List[str]] = None,
228
+ status: Optional[CollectionStatus] = None,
229
+ description: Optional[Dict[str, str]] = None,
230
+ image_url: Optional[str] = None,
231
+ image: Optional[str] = None,
232
+ avatar_url: Optional[str] = None,
233
+ meta_data: Optional[dict] = None,
234
+ ) -> None:
235
+ """Update an existing custom theme.
236
+
237
+ Args:
238
+ theme_id: Unique identifier for the custom theme
239
+ title: Localized title (optional)
240
+ stock_ids: List of stock IDs (optional)
241
+ status: Collection status (optional)
242
+ description: Localized description (optional)
243
+ image_url: Image URL (optional)
244
+ image: Image data (optional)
245
+ avatar_url: Avatar URL (optional)
246
+ meta_data: Additional metadata (optional)
247
+ """
248
+ body: dict = {}
249
+
250
+ if title is not None:
251
+ body["title"] = title
252
+ if stock_ids is not None:
253
+ body["stockIds"] = stock_ids
254
+ if status is not None:
255
+ body["status"] = status.value
256
+ if description is not None:
257
+ body["description"] = description
258
+ if image_url is not None:
259
+ body["image_url"] = image_url
260
+ if image is not None:
261
+ body["image"] = image
262
+ if avatar_url is not None:
263
+ body["avatar_url"] = avatar_url
264
+ if meta_data is not None:
265
+ body["meta_data"] = meta_data
266
+
267
+ self._client.patch(f"v1/custom-theme/{theme_id}", json=body)
268
+
269
+ def get_sectors(self, region: Region, locale: Locale = "en") -> List[Collection]:
167
270
  """Retrieve a list of sectors along with the number of stocks in each.
168
271
 
169
272
  Args:
@@ -171,16 +274,16 @@ class CollectionsClient:
171
274
  locale: Locale code (tr, en) (default: en)
172
275
 
173
276
  Returns:
174
- List[Sector]: List of sectors
277
+ List[Collection]: List of sectors
175
278
  """
176
279
  params = {"region": region.value, "locale": locale}
177
280
 
178
281
  response = self._client.get("v1/sector", params=params)
179
- return [Sector(**sector) for sector in response]
282
+ return [Collection(**sector) for sector in response]
180
283
 
181
284
  def get_sector_detail(
182
285
  self, sector_id: str, region: Region, locale: Locale = "en"
183
- ) -> SectorDetail:
286
+ ) -> CollectionDetail:
184
287
  """Retrieve detailed information about a specific sector.
185
288
 
186
289
  Args:
@@ -189,9 +292,9 @@ class CollectionsClient:
189
292
  locale: Locale code (tr, en) (default: en)
190
293
 
191
294
  Returns:
192
- SectorDetail: Detailed sector information
295
+ CollectionDetail: Detailed sector information
193
296
  """
194
297
  params = {"locale": locale, "region": region.value}
195
298
 
196
299
  response = self._client.get(f"v1/sector/{sector_id}", params=params)
197
- return SectorDetail(**response)
300
+ return CollectionDetail(**response)
@@ -1,6 +1,6 @@
1
1
  """Financials client for Laplace API."""
2
2
 
3
- from typing import List
3
+ from typing import List, Optional
4
4
 
5
5
  from laplace.base import BaseClient
6
6
 
@@ -41,14 +41,17 @@ class FinancialsClient:
41
41
  return [StockPeerFinancialRatioComparison(**item) for item in resp]
42
42
 
43
43
  def get_historical_ratios(
44
- self, symbol: str, keys: List[str], region: Region, locale: Locale
44
+ self, symbol: str, keys: List[str], region: Region, locale: Optional[Locale] = None
45
45
  ) -> List[StockHistoricalRatios]:
46
46
  params = {
47
47
  "symbol": symbol,
48
48
  "region": region.value,
49
- "locale": locale,
50
49
  "slugs": ",".join(keys),
51
50
  }
51
+
52
+ if locale:
53
+ params["locale"] = locale
54
+
52
55
  resp = self._client.get("v2/stock/historical-ratios", params=params)
53
56
  return [StockHistoricalRatios(**item) for item in resp]
54
57