alpaca-py-nopandas 0.1.0__py3-none-any.whl

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 (62) hide show
  1. alpaca/__init__.py +2 -0
  2. alpaca/broker/__init__.py +8 -0
  3. alpaca/broker/client.py +2360 -0
  4. alpaca/broker/enums.py +528 -0
  5. alpaca/broker/models/__init__.py +7 -0
  6. alpaca/broker/models/accounts.py +347 -0
  7. alpaca/broker/models/cip.py +265 -0
  8. alpaca/broker/models/documents.py +159 -0
  9. alpaca/broker/models/funding.py +114 -0
  10. alpaca/broker/models/journals.py +71 -0
  11. alpaca/broker/models/rebalancing.py +80 -0
  12. alpaca/broker/models/trading.py +13 -0
  13. alpaca/broker/requests.py +1135 -0
  14. alpaca/common/__init__.py +6 -0
  15. alpaca/common/constants.py +13 -0
  16. alpaca/common/enums.py +64 -0
  17. alpaca/common/exceptions.py +47 -0
  18. alpaca/common/models.py +21 -0
  19. alpaca/common/requests.py +82 -0
  20. alpaca/common/rest.py +438 -0
  21. alpaca/common/types.py +7 -0
  22. alpaca/common/utils.py +89 -0
  23. alpaca/data/__init__.py +5 -0
  24. alpaca/data/enums.py +184 -0
  25. alpaca/data/historical/__init__.py +13 -0
  26. alpaca/data/historical/corporate_actions.py +76 -0
  27. alpaca/data/historical/crypto.py +299 -0
  28. alpaca/data/historical/news.py +63 -0
  29. alpaca/data/historical/option.py +230 -0
  30. alpaca/data/historical/screener.py +72 -0
  31. alpaca/data/historical/stock.py +226 -0
  32. alpaca/data/historical/utils.py +30 -0
  33. alpaca/data/live/__init__.py +11 -0
  34. alpaca/data/live/crypto.py +168 -0
  35. alpaca/data/live/news.py +62 -0
  36. alpaca/data/live/option.py +88 -0
  37. alpaca/data/live/stock.py +199 -0
  38. alpaca/data/live/websocket.py +390 -0
  39. alpaca/data/mappings.py +84 -0
  40. alpaca/data/models/__init__.py +7 -0
  41. alpaca/data/models/bars.py +83 -0
  42. alpaca/data/models/base.py +45 -0
  43. alpaca/data/models/corporate_actions.py +309 -0
  44. alpaca/data/models/news.py +90 -0
  45. alpaca/data/models/orderbooks.py +59 -0
  46. alpaca/data/models/quotes.py +78 -0
  47. alpaca/data/models/screener.py +68 -0
  48. alpaca/data/models/snapshots.py +132 -0
  49. alpaca/data/models/trades.py +204 -0
  50. alpaca/data/requests.py +580 -0
  51. alpaca/data/timeframe.py +148 -0
  52. alpaca/py.typed +0 -0
  53. alpaca/trading/__init__.py +5 -0
  54. alpaca/trading/client.py +784 -0
  55. alpaca/trading/enums.py +412 -0
  56. alpaca/trading/models.py +697 -0
  57. alpaca/trading/requests.py +604 -0
  58. alpaca/trading/stream.py +225 -0
  59. alpaca_py_nopandas-0.1.0.dist-info/LICENSE +201 -0
  60. alpaca_py_nopandas-0.1.0.dist-info/METADATA +299 -0
  61. alpaca_py_nopandas-0.1.0.dist-info/RECORD +62 -0
  62. alpaca_py_nopandas-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,784 @@
1
+ import json
2
+ import warnings
3
+ from typing import List, Optional, Union
4
+ from uuid import UUID
5
+
6
+ from pydantic import TypeAdapter
7
+
8
+ from alpaca.common import RawData
9
+ from alpaca.common.enums import BaseURL
10
+ from alpaca.common.rest import RESTClient
11
+ from alpaca.common.utils import (
12
+ validate_symbol_or_asset_id,
13
+ validate_symbol_or_contract_id,
14
+ validate_uuid_id_param,
15
+ )
16
+ from alpaca.trading.models import (
17
+ AccountConfiguration,
18
+ Asset,
19
+ Calendar,
20
+ Clock,
21
+ ClosePositionResponse,
22
+ CorporateActionAnnouncement,
23
+ OptionContract,
24
+ OptionContractsResponse,
25
+ Order,
26
+ PortfolioHistory,
27
+ Position,
28
+ TradeAccount,
29
+ Watchlist,
30
+ )
31
+ from alpaca.trading.requests import (
32
+ CancelOrderResponse,
33
+ ClosePositionRequest,
34
+ CreateWatchlistRequest,
35
+ GetAssetsRequest,
36
+ GetCalendarRequest,
37
+ GetCorporateAnnouncementsRequest,
38
+ GetOptionContractsRequest,
39
+ GetOrderByIdRequest,
40
+ GetOrdersRequest,
41
+ GetPortfolioHistoryRequest,
42
+ OrderRequest,
43
+ ReplaceOrderRequest,
44
+ UpdateWatchlistRequest,
45
+ )
46
+
47
+
48
+ class TradingClient(RESTClient):
49
+ """
50
+ A client to interact with the trading API, in both paper and live mode.
51
+ """
52
+
53
+ def __init__(
54
+ self,
55
+ api_key: Optional[str] = None,
56
+ secret_key: Optional[str] = None,
57
+ oauth_token: Optional[str] = None,
58
+ paper: bool = True,
59
+ raw_data: bool = False,
60
+ url_override: Optional[str] = None,
61
+ ) -> None:
62
+ """
63
+ Instantiates a client for trading and managing personal brokerage accounts.
64
+
65
+ Args:
66
+ api_key (Optional[str]): The API key for trading. Use paper keys if paper is set to true.
67
+ secret_key (Optional[str]): The secret key for trading. Use paper keys if paper is set to true.
68
+ oauth_token (Optional[str]): The oauth token for trading on behalf of end user.
69
+ paper (bool): True is paper trading should be enabled.
70
+ raw_data (bool): Whether API responses should be wrapped in data models or returned raw.
71
+ This has not been implemented yet.
72
+ url_override (Optional[str]): If specified allows you to override the base url the client points to for proxy/testing.
73
+ """
74
+ super().__init__(
75
+ api_key=api_key,
76
+ secret_key=secret_key,
77
+ oauth_token=oauth_token,
78
+ api_version="v2",
79
+ base_url=(
80
+ url_override
81
+ if url_override
82
+ else BaseURL.TRADING_PAPER if paper else BaseURL.TRADING_LIVE
83
+ ),
84
+ sandbox=paper,
85
+ raw_data=raw_data,
86
+ )
87
+
88
+ # ############################## ORDERS ################################# #
89
+
90
+ def submit_order(self, order_data: OrderRequest) -> Union[Order, RawData]:
91
+ """Creates an order to buy or sell an asset.
92
+
93
+ Args:
94
+ order_data (alpaca.trading.requests.OrderRequest): The request data for creating a new order.
95
+
96
+ Returns:
97
+ alpaca.trading.models.Order: The resulting submitted order.
98
+ """
99
+ data = order_data.to_request_fields()
100
+ response = self.post("/orders", data)
101
+
102
+ if self._use_raw_data:
103
+ return response
104
+
105
+ return Order(**response)
106
+
107
+ def get_orders(
108
+ self, filter: Optional[GetOrdersRequest] = None
109
+ ) -> Union[List[Order], RawData]:
110
+ """
111
+ Returns all orders. Orders can be filtered by parameters.
112
+
113
+ Args:
114
+ filter (Optional[GetOrdersRequest]): The parameters to filter the orders with.
115
+
116
+ Returns:
117
+ List[alpaca.trading.models.Order]: The queried orders.
118
+ """
119
+ # checking to see if we specified at least one param
120
+ params = filter.to_request_fields() if filter is not None else {}
121
+
122
+ if "symbols" in params and isinstance(params["symbols"], list):
123
+ params["symbols"] = ",".join(params["symbols"])
124
+
125
+ response = self.get("/orders", params)
126
+
127
+ if self._use_raw_data:
128
+ return response
129
+
130
+ return TypeAdapter(List[Order]).validate_python(response)
131
+
132
+ def get_order_by_id(
133
+ self, order_id: Union[UUID, str], filter: Optional[GetOrderByIdRequest] = None
134
+ ) -> Union[Order, RawData]:
135
+ """
136
+ Returns a specific order by its order id.
137
+
138
+ Args:
139
+ order_id (Union[UUID, str]): The unique uuid identifier for the order.
140
+ filter (Optional[GetOrderByIdRequest]): The parameters for the query.
141
+
142
+ Returns:
143
+ alpaca.trading.models.Order: The order that was queried.
144
+ """
145
+ # checking to see if we specified at least one param
146
+ params = filter.to_request_fields() if filter is not None else {}
147
+
148
+ order_id = validate_uuid_id_param(order_id, "order_id")
149
+
150
+ response = self.get(f"/orders/{order_id}", params)
151
+
152
+ if self._use_raw_data:
153
+ return response
154
+
155
+ return Order(**response)
156
+
157
+ def get_order_by_client_id(self, client_id: str) -> Union[Order, RawData]:
158
+ """
159
+ Returns a specific order by its client order id.
160
+
161
+ Args:
162
+ client_id (str): The client order identifier for the order.
163
+
164
+ Returns:
165
+ alpaca.trading.models.Order: The queried order.
166
+ """
167
+ params = {"client_order_id": client_id}
168
+
169
+ response = self.get(f"/orders:by_client_order_id", params)
170
+
171
+ if self._use_raw_data:
172
+ return response
173
+
174
+ return Order(**response)
175
+
176
+ def replace_order_by_id(
177
+ self,
178
+ order_id: Union[UUID, str],
179
+ order_data: Optional[ReplaceOrderRequest] = None,
180
+ ) -> Union[Order, RawData]:
181
+ """
182
+ Updates an order with new parameters.
183
+
184
+ Args:
185
+ order_id (Union[UUID, str]): The unique uuid identifier for the order being replaced.
186
+ order_data (Optional[ReplaceOrderRequest]): The parameters we wish to update.
187
+
188
+ Returns:
189
+ alpaca.trading.models.Order: The updated order.
190
+ """
191
+ # checking to see if we specified at least one param
192
+ params = order_data.to_request_fields() if order_data is not None else {}
193
+
194
+ order_id = validate_uuid_id_param(order_id, "order_id")
195
+
196
+ response = self.patch(f"/orders/{order_id}", params)
197
+
198
+ if self._use_raw_data:
199
+ return response
200
+
201
+ return Order(**response)
202
+
203
+ def cancel_orders(self) -> Union[List[CancelOrderResponse], RawData]:
204
+ """
205
+ Cancels all orders.
206
+
207
+ Returns:
208
+ List[CancelOrderResponse]: The list of HTTP statuses for each order attempted to be cancelled.
209
+ """
210
+ response = self.delete(f"/orders")
211
+
212
+ if self._use_raw_data:
213
+ return response
214
+
215
+ return TypeAdapter(List[CancelOrderResponse]).validate_python(response)
216
+
217
+ def cancel_order_by_id(self, order_id: Union[UUID, str]) -> None:
218
+ """
219
+ Cancels a specific order by its order id.
220
+
221
+ Args:
222
+ order_id (Union[UUID, str]): The unique uuid identifier of the order being cancelled.
223
+
224
+ Returns:
225
+ None
226
+ """
227
+ order_id = validate_uuid_id_param(order_id, "order_id")
228
+
229
+ # TODO: Should ideally return some information about the order's cancel status. (Issue #78).
230
+ # TODO: Currently no way to retrieve status details for empty responses with base REST implementation
231
+ self.delete(f"/orders/{order_id}")
232
+
233
+ # ############################## POSITIONS ################################# #
234
+
235
+ def get_all_positions(
236
+ self,
237
+ ) -> Union[List[Position], RawData]:
238
+ """
239
+ Gets all the current open positions.
240
+
241
+ Returns:
242
+ List[Position]: List of open positions.
243
+ """
244
+ response = self.get("/positions")
245
+
246
+ if self._use_raw_data:
247
+ return response
248
+
249
+ return TypeAdapter(List[Position]).validate_python(response)
250
+
251
+ def get_open_position(
252
+ self, symbol_or_asset_id: Union[UUID, str]
253
+ ) -> Union[Position, RawData]:
254
+ """
255
+ Gets the open position for an account for a single asset. Throws an APIError if the position does not exist.
256
+
257
+ Args:
258
+ symbol_or_asset_id (Union[UUID, str]): The symbol name of asset id of the position to get.
259
+
260
+ Returns:
261
+ Position: Open position of the asset.
262
+ """
263
+ symbol_or_asset_id = validate_symbol_or_asset_id(symbol_or_asset_id)
264
+ response = self.get(f"/positions/{symbol_or_asset_id}")
265
+
266
+ if self._use_raw_data:
267
+ return response
268
+
269
+ return Position(**response)
270
+
271
+ def close_all_positions(
272
+ self, cancel_orders: Optional[bool] = None
273
+ ) -> Union[List[ClosePositionResponse], RawData]:
274
+ """
275
+ Liquidates all positions for an account.
276
+
277
+ Places an order for each open position to liquidate.
278
+
279
+ Args:
280
+ cancel_orders (Optional[bool]): If true is specified, cancel all open orders before liquidating all positions.
281
+
282
+ Returns:
283
+ List[ClosePositionResponse]: A list of responses from each closed position containing the status code and
284
+ order id.
285
+ """
286
+ response = self.delete(
287
+ "/positions",
288
+ {"cancel_orders": cancel_orders} if cancel_orders else None,
289
+ )
290
+
291
+ if self._use_raw_data:
292
+ return response
293
+
294
+ return TypeAdapter(List[ClosePositionResponse]).validate_python(response)
295
+
296
+ def close_position(
297
+ self,
298
+ symbol_or_asset_id: Union[UUID, str],
299
+ close_options: Optional[ClosePositionRequest] = None,
300
+ ) -> Union[Order, RawData]:
301
+ """
302
+ Liquidates the position for a single asset.
303
+
304
+ Places a single order to close the position for the asset.
305
+
306
+ **This method will throw an error if the position does not exist!**
307
+
308
+ Args:
309
+ symbol_or_asset_id (Union[UUID, str]): The symbol name of asset id of the position to close.
310
+ close_options: The various close position request parameters.
311
+
312
+ Returns:
313
+ alpaca.trading.models.Order: The order that was placed to close the position.
314
+ """
315
+ symbol_or_asset_id = validate_symbol_or_asset_id(symbol_or_asset_id)
316
+ response = self.delete(
317
+ f"/positions/{symbol_or_asset_id}",
318
+ close_options.to_request_fields() if close_options else {},
319
+ )
320
+
321
+ if self._use_raw_data:
322
+ return response
323
+
324
+ return Order(**response)
325
+
326
+ def exercise_options_position(
327
+ self,
328
+ symbol_or_contract_id: Union[UUID, str],
329
+ ) -> None:
330
+ """
331
+ This endpoint enables users to exercise a held option contract, converting it into the underlying asset based on the specified terms.
332
+ All available held shares of this option contract will be exercised.
333
+ By default, Alpaca will automatically exercise in-the-money (ITM) contracts at expiry.
334
+ Exercise requests will be processed immediately once received. Exercise requests submitted outside market hours will be rejected.
335
+ To cancel an exercise request or to submit a Do-not-exercise (DNE) instruction, please contact our support team.
336
+
337
+ Args:
338
+ symbol_or_contract_id (Union[UUID, str]): Option contract symbol or ID.
339
+
340
+ Returns:
341
+ None
342
+ """
343
+ symbol_or_contract_id = validate_symbol_or_contract_id(symbol_or_contract_id)
344
+ self.post(
345
+ f"/positions/{symbol_or_contract_id}/exercise",
346
+ )
347
+
348
+ # ############################## Portfolio ################################# #
349
+
350
+ def get_portfolio_history(
351
+ self,
352
+ history_filter: Optional[GetPortfolioHistoryRequest] = None,
353
+ ) -> Union[PortfolioHistory, RawData]:
354
+ """
355
+ Gets the portfolio history statistics for an account.
356
+
357
+ Args:
358
+ account_id (Union[UUID, str]): The ID of the Account to get the portfolio history for.
359
+ history_filter: The various portfolio history request parameters.
360
+
361
+ Returns:
362
+ PortfolioHistory: The portfolio history statistics for the account.
363
+ """
364
+ response = self.get(
365
+ f"/account/portfolio/history",
366
+ history_filter.to_request_fields() if history_filter else {},
367
+ )
368
+
369
+ if self._use_raw_data:
370
+ return response
371
+
372
+ return PortfolioHistory(**response)
373
+
374
+ # ############################## Assets ################################# #
375
+
376
+ def get_all_assets(
377
+ self, filter: Optional[GetAssetsRequest] = None
378
+ ) -> Union[List[Asset], RawData]:
379
+ """
380
+ The assets API serves as the master list of assets available for trade and data consumption from Alpaca.
381
+ Some assets are not tradable with Alpaca. These assets will be marked with the flag tradable=false.
382
+
383
+ Args:
384
+ filter (Optional[GetAssetsRequest]): The parameters that can be assets can be queried by.
385
+
386
+ Returns:
387
+ List[Asset]: The list of assets.
388
+ """
389
+ # checking to see if we specified at least one param
390
+ params = filter.to_request_fields() if filter is not None else {}
391
+
392
+ response = self.get(f"/assets", params)
393
+
394
+ if self._use_raw_data:
395
+ return response
396
+
397
+ return TypeAdapter(List[Asset]).validate_python(response)
398
+
399
+ def get_asset(self, symbol_or_asset_id: Union[UUID, str]) -> Union[Asset, RawData]:
400
+ """
401
+ Returns a specific asset by its symbol or asset id. If the specified asset does not exist
402
+ a 404 error will be thrown.
403
+
404
+ Args:
405
+ symbol_or_asset_id (Union[UUID, str]): The symbol or asset id for the specified asset
406
+
407
+ Returns:
408
+ Asset: The asset if it exists.
409
+ """
410
+
411
+ symbol_or_asset_id = validate_symbol_or_asset_id(symbol_or_asset_id)
412
+
413
+ response = self.get(f"/assets/{symbol_or_asset_id}")
414
+
415
+ if self._use_raw_data:
416
+ return response
417
+
418
+ return Asset(**response)
419
+
420
+ # ############################## CLOCK & CALENDAR ################################# #
421
+
422
+ def get_clock(self) -> Union[Clock, RawData]:
423
+ """
424
+ Gets the current market timestamp, whether or not the market is currently open, as well as the times
425
+ of the next market open and close.
426
+
427
+ Returns:
428
+ Clock: The market Clock data
429
+ """
430
+
431
+ response = self.get("/clock")
432
+
433
+ if self._use_raw_data:
434
+ return response
435
+
436
+ return Clock(**response)
437
+
438
+ def get_calendar(
439
+ self,
440
+ filters: Optional[GetCalendarRequest] = None,
441
+ ) -> Union[List[Calendar], RawData]:
442
+ """
443
+ The calendar API serves the full list of market days from 1970 to 2029. It can also be queried by specifying a
444
+ start and/or end time to narrow down the results.
445
+
446
+ In addition to the dates, the response also contains the specific open and close times for the market days,
447
+ taking into account early closures.
448
+
449
+ Args:
450
+ filters: Any optional filters to limit the returned market days
451
+
452
+ Returns:
453
+ List[Calendar]: A list of Calendar objects representing the market days.
454
+ """
455
+
456
+ result = self.get("/calendar", filters.to_request_fields() if filters else {})
457
+
458
+ if self._use_raw_data:
459
+ return result
460
+
461
+ return TypeAdapter(List[Calendar]).validate_python(result)
462
+
463
+ # ############################## ACCOUNT ################################# #
464
+
465
+ def get_account(self) -> Union[TradeAccount, RawData]:
466
+ """
467
+ Returns account details. Contains information like buying power,
468
+ number of day trades, and account status.
469
+
470
+ Returns:
471
+ alpaca.trading.models.TradeAccount: The account details
472
+ """
473
+
474
+ response = self.get("/account")
475
+
476
+ if self._use_raw_data:
477
+ return response
478
+
479
+ return TradeAccount(**response)
480
+
481
+ def get_account_configurations(self) -> Union[AccountConfiguration, RawData]:
482
+ """
483
+ Returns account configuration details. Contains information like shorting, margin multiplier
484
+ trader confirmation emails, and Pattern Day Trading (PDT) checks.
485
+
486
+ Returns:
487
+ alpaca.broker.models.AccountConfiguration: The account configuration details
488
+ """
489
+ response = self.get("/account/configurations")
490
+
491
+ if self._use_raw_data:
492
+ return response
493
+
494
+ return AccountConfiguration(**response)
495
+
496
+ def set_account_configurations(
497
+ self, account_configurations: AccountConfiguration
498
+ ) -> Union[AccountConfiguration, RawData]:
499
+ """
500
+ Returns account configuration details. Contains information like shorting, margin multiplier
501
+ trader confirmation emails, and Pattern Day Trading (PDT) checks.
502
+
503
+ Returns:
504
+ alpaca.broker.models.TradeAccountConfiguration: The account configuration details
505
+ """
506
+ response = self.patch(
507
+ "/account/configurations", data=account_configurations.model_dump()
508
+ )
509
+
510
+ if self._use_raw_data:
511
+ return response
512
+
513
+ return AccountConfiguration(**response)
514
+
515
+ # ############################## WATCHLIST ################################# #
516
+
517
+ def get_watchlists(
518
+ self,
519
+ ) -> Union[List[Watchlist], RawData]:
520
+ """
521
+ Returns all watchlists.
522
+
523
+ Returns:
524
+ List[Watchlist]: The list of all watchlists.
525
+ """
526
+
527
+ result = self.get(f"/watchlists")
528
+
529
+ if self._use_raw_data:
530
+ return result
531
+
532
+ return TypeAdapter(List[Watchlist]).validate_python(result)
533
+
534
+ def get_watchlist_by_id(
535
+ self,
536
+ watchlist_id: Union[UUID, str],
537
+ ) -> Union[Watchlist, RawData]:
538
+ """
539
+ Returns a specific watchlist by its id.
540
+
541
+ Args:
542
+ watchlist_id (Union[UUID, str]): The watchlist to retrieve.
543
+
544
+ Returns:
545
+ Watchlist: The watchlist.
546
+ """
547
+ watchlist_id = validate_uuid_id_param(watchlist_id, "watchlist_id")
548
+
549
+ result = self.get(f"/watchlists/{watchlist_id}")
550
+
551
+ if self._use_raw_data:
552
+ return result
553
+
554
+ return Watchlist(**result)
555
+
556
+ def create_watchlist(
557
+ self,
558
+ watchlist_data: CreateWatchlistRequest,
559
+ ) -> Union[Watchlist, RawData]:
560
+ """
561
+ Creates a new watchlist.
562
+
563
+ Args:
564
+ watchlist_data (CreateWatchlistRequest): The watchlist to create.
565
+
566
+ Returns:
567
+ Watchlist: The new watchlist.
568
+ """
569
+ result = self.post(
570
+ "/watchlists",
571
+ watchlist_data.to_request_fields(),
572
+ )
573
+
574
+ if self._use_raw_data:
575
+ return result
576
+
577
+ return Watchlist(**result)
578
+
579
+ def update_watchlist_by_id(
580
+ self,
581
+ watchlist_id: Union[UUID, str],
582
+ # Might be worth taking a union of this and Watchlist itself; but then we should make a change like that SDK
583
+ # wide. Probably a good 0.2.x change
584
+ watchlist_data: UpdateWatchlistRequest,
585
+ ) -> Union[Watchlist, RawData]:
586
+ """
587
+ Updates a watchlist with new data.
588
+
589
+ Args:
590
+ watchlist_id (Union[UUID, str]): The watchlist to be updated.
591
+ watchlist_data (UpdateWatchlistRequest): The new watchlist data.
592
+
593
+ Returns:
594
+ Watchlist: The watchlist with updated data.
595
+ """
596
+ watchlist_id = validate_uuid_id_param(watchlist_id, "watchlist_id")
597
+
598
+ result = self.put(
599
+ f"/watchlists/{watchlist_id}",
600
+ watchlist_data.to_request_fields(),
601
+ )
602
+
603
+ if self._use_raw_data:
604
+ return result
605
+
606
+ return Watchlist(**result)
607
+
608
+ def add_asset_to_watchlist_by_id(
609
+ self,
610
+ watchlist_id: Union[UUID, str],
611
+ symbol: str,
612
+ ) -> Union[Watchlist, RawData]:
613
+ """
614
+ Adds an asset by its symbol to a specified watchlist.
615
+
616
+ Args:
617
+ watchlist_id (Union[UUID, str]): The watchlist to add the symbol to.
618
+ symbol (str): The symbol for the asset to add.
619
+
620
+ Returns:
621
+ Watchlist: The updated watchlist.
622
+ """
623
+ watchlist_id = validate_uuid_id_param(watchlist_id, "watchlist_id")
624
+
625
+ params = {"symbol": symbol}
626
+
627
+ result = self.post(f"/watchlists/{watchlist_id}", params)
628
+
629
+ if self._use_raw_data:
630
+ return result
631
+
632
+ return Watchlist(**result)
633
+
634
+ def delete_watchlist_by_id(
635
+ self,
636
+ watchlist_id: Union[UUID, str],
637
+ ) -> None:
638
+ """
639
+ Deletes a watchlist. This is permanent.
640
+
641
+ Args:
642
+ watchlist_id (Union[UUID, str]): The watchlist to delete.
643
+
644
+ Returns:
645
+ None
646
+ """
647
+ watchlist_id = validate_uuid_id_param(watchlist_id, "watchlist_id")
648
+
649
+ self.delete(f"/watchlists/{watchlist_id}")
650
+
651
+ def remove_asset_from_watchlist_by_id(
652
+ self,
653
+ watchlist_id: Union[UUID, str],
654
+ symbol: str,
655
+ ) -> Union[Watchlist, RawData]:
656
+ """
657
+ Removes an asset from a watchlist.
658
+
659
+ Args:
660
+ watchlist_id (Union[UUID, str]): The watchlist to remove the asset from.
661
+ symbol (str): The symbol for the asset to add.
662
+
663
+ Returns:
664
+ Watchlist: The updated watchlist.
665
+ """
666
+ watchlist_id = validate_uuid_id_param(watchlist_id, "watchlist_id")
667
+
668
+ result = self.delete(f"/watchlists/{watchlist_id}/{symbol}")
669
+
670
+ if self._use_raw_data:
671
+ return result
672
+
673
+ return Watchlist(**result)
674
+
675
+ # ############################## CORPORATE ACTIONS ################################# #
676
+
677
+ def get_corporate_announcements(
678
+ self, filter: GetCorporateAnnouncementsRequest
679
+ ) -> Union[List[CorporateActionAnnouncement], RawData]:
680
+ """
681
+ DEPRECATED: Please use the new corporate actions endpoint instead.
682
+ alpaca.data.historical.corporate_actions.CorporateActionsClient.get_corporate_actions()
683
+ ref. https://docs.alpaca.markets/reference/corporateactions-1
684
+
685
+ Returns corporate action announcements data given specified search criteria.
686
+
687
+ Args:
688
+ filter (GetCorporateAnnouncementsRequest): The parameters to filter the search by.
689
+ Returns:
690
+ List[CorporateActionAnnouncement]: The resulting announcements from the search.
691
+ """
692
+ warnings.warn(
693
+ "get_corporate_announcements is deprecated and will be removed in a future version."
694
+ "Please use alpaca.data.historical.corporate_actions.CorporateActionsClient.get_corporate_actions() instead",
695
+ DeprecationWarning,
696
+ )
697
+
698
+ params = filter.to_request_fields() if filter else {}
699
+
700
+ if "ca_types" in params and isinstance(params["ca_types"], list):
701
+ params["ca_types"] = ",".join(params["ca_types"])
702
+
703
+ response = self.get("/corporate_actions/announcements", params)
704
+
705
+ if self._use_raw_data:
706
+ return response
707
+
708
+ return TypeAdapter(List[CorporateActionAnnouncement]).validate_python(response)
709
+
710
+ def get_corporate_announcement_by_id(
711
+ self, corporate_announcment_id: Union[UUID, str]
712
+ ) -> Union[CorporateActionAnnouncement, RawData]:
713
+ """
714
+ Returns a specific corporate action announcement.
715
+ Args:
716
+ corporate_announcment_id: The id of the desired corporate action announcement
717
+ Returns:
718
+ CorporateActionAnnouncement: The corporate action queried.
719
+ """
720
+ corporate_announcment_id = validate_uuid_id_param(
721
+ corporate_announcment_id, "corporate_announcment_id"
722
+ )
723
+
724
+ response = self.get(
725
+ f"/corporate_actions/announcements/{corporate_announcment_id}"
726
+ )
727
+
728
+ if self._use_raw_data:
729
+ return response
730
+
731
+ return CorporateActionAnnouncement(**response)
732
+
733
+ # ############################## OPTIONS CONTRACTS ################################# #
734
+
735
+ def get_option_contracts(
736
+ self, request: GetOptionContractsRequest
737
+ ) -> Union[OptionContractsResponse, RawData]:
738
+ """
739
+ The option contracts API serves as the master list of option contracts available for trade and data consumption from Alpaca.
740
+
741
+ Args:
742
+ request (GetOptionContractsRequest): The parameters that option contracts can be queried by.
743
+
744
+ Returns:
745
+ OptionContracts (Union[OptionContractsResponse, RawData]): The object includes list of option contracts.
746
+ """
747
+ if request is None:
748
+ raise ValueError("request (GetOptionContractsRequest) is required")
749
+
750
+ params = request.to_request_fields()
751
+
752
+ if "underlying_symbols" in params and isinstance(
753
+ request.underlying_symbols, list
754
+ ):
755
+ params["underlying_symbols"] = ",".join(request.underlying_symbols)
756
+
757
+ response = self.get("/options/contracts", params)
758
+
759
+ if self._use_raw_data:
760
+ return response
761
+
762
+ return TypeAdapter(OptionContractsResponse).validate_python(response)
763
+
764
+ def get_option_contract(
765
+ self, symbol_or_id: Union[UUID, str]
766
+ ) -> Union[OptionContract, RawData]:
767
+ """
768
+ The option contracts API serves as the master list of option contracts available for trade and data consumption from Alpaca.
769
+
770
+ Args:
771
+ symbol_or_id (Union[UUID, str]): The symbol or id of the option contract to retrieve.
772
+
773
+ Returns:
774
+ OptionContracts (Union[OptionContracts, RawData]): The list of option contracts.
775
+ """
776
+ if symbol_or_id == "":
777
+ raise ValueError("symbol_or_id is required")
778
+
779
+ response = self.get(f"/options/contracts/{symbol_or_id}")
780
+
781
+ if self._use_raw_data:
782
+ return response
783
+
784
+ return TypeAdapter(OptionContract).validate_python(response)