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.
- alpaca/__init__.py +2 -0
- alpaca/broker/__init__.py +8 -0
- alpaca/broker/client.py +2360 -0
- alpaca/broker/enums.py +528 -0
- alpaca/broker/models/__init__.py +7 -0
- alpaca/broker/models/accounts.py +347 -0
- alpaca/broker/models/cip.py +265 -0
- alpaca/broker/models/documents.py +159 -0
- alpaca/broker/models/funding.py +114 -0
- alpaca/broker/models/journals.py +71 -0
- alpaca/broker/models/rebalancing.py +80 -0
- alpaca/broker/models/trading.py +13 -0
- alpaca/broker/requests.py +1135 -0
- alpaca/common/__init__.py +6 -0
- alpaca/common/constants.py +13 -0
- alpaca/common/enums.py +64 -0
- alpaca/common/exceptions.py +47 -0
- alpaca/common/models.py +21 -0
- alpaca/common/requests.py +82 -0
- alpaca/common/rest.py +438 -0
- alpaca/common/types.py +7 -0
- alpaca/common/utils.py +89 -0
- alpaca/data/__init__.py +5 -0
- alpaca/data/enums.py +184 -0
- alpaca/data/historical/__init__.py +13 -0
- alpaca/data/historical/corporate_actions.py +76 -0
- alpaca/data/historical/crypto.py +299 -0
- alpaca/data/historical/news.py +63 -0
- alpaca/data/historical/option.py +230 -0
- alpaca/data/historical/screener.py +72 -0
- alpaca/data/historical/stock.py +226 -0
- alpaca/data/historical/utils.py +30 -0
- alpaca/data/live/__init__.py +11 -0
- alpaca/data/live/crypto.py +168 -0
- alpaca/data/live/news.py +62 -0
- alpaca/data/live/option.py +88 -0
- alpaca/data/live/stock.py +199 -0
- alpaca/data/live/websocket.py +390 -0
- alpaca/data/mappings.py +84 -0
- alpaca/data/models/__init__.py +7 -0
- alpaca/data/models/bars.py +83 -0
- alpaca/data/models/base.py +45 -0
- alpaca/data/models/corporate_actions.py +309 -0
- alpaca/data/models/news.py +90 -0
- alpaca/data/models/orderbooks.py +59 -0
- alpaca/data/models/quotes.py +78 -0
- alpaca/data/models/screener.py +68 -0
- alpaca/data/models/snapshots.py +132 -0
- alpaca/data/models/trades.py +204 -0
- alpaca/data/requests.py +580 -0
- alpaca/data/timeframe.py +148 -0
- alpaca/py.typed +0 -0
- alpaca/trading/__init__.py +5 -0
- alpaca/trading/client.py +784 -0
- alpaca/trading/enums.py +412 -0
- alpaca/trading/models.py +697 -0
- alpaca/trading/requests.py +604 -0
- alpaca/trading/stream.py +225 -0
- alpaca_py_nopandas-0.1.0.dist-info/LICENSE +201 -0
- alpaca_py_nopandas-0.1.0.dist-info/METADATA +299 -0
- alpaca_py_nopandas-0.1.0.dist-info/RECORD +62 -0
- alpaca_py_nopandas-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,1135 @@
|
|
1
|
+
from datetime import date, datetime
|
2
|
+
from typing import Any, Dict, List, Optional, Union
|
3
|
+
from uuid import UUID
|
4
|
+
|
5
|
+
from pydantic import field_serializer, field_validator, model_validator
|
6
|
+
|
7
|
+
from alpaca.broker.enums import (
|
8
|
+
AccountEntities,
|
9
|
+
BankAccountType,
|
10
|
+
CalendarSubType,
|
11
|
+
DocumentType,
|
12
|
+
DriftBandSubType,
|
13
|
+
FeePaymentMethod,
|
14
|
+
FundingSource,
|
15
|
+
IdentifierType,
|
16
|
+
JournalEntryType,
|
17
|
+
JournalStatus,
|
18
|
+
PortfolioStatus,
|
19
|
+
RebalancingConditionsType,
|
20
|
+
RunType,
|
21
|
+
TradeDocumentType,
|
22
|
+
TransferDirection,
|
23
|
+
TransferTiming,
|
24
|
+
TransferType,
|
25
|
+
UploadDocumentMimeType,
|
26
|
+
UploadDocumentSubType,
|
27
|
+
VisaType,
|
28
|
+
WeightType,
|
29
|
+
)
|
30
|
+
from alpaca.broker.models.accounts import (
|
31
|
+
AccountDocument,
|
32
|
+
Agreement,
|
33
|
+
Contact,
|
34
|
+
Disclosures,
|
35
|
+
Identity,
|
36
|
+
TrustedContact,
|
37
|
+
)
|
38
|
+
from alpaca.broker.models.documents import W8BenDocument
|
39
|
+
from alpaca.common.enums import Sort, SupportedCurrencies
|
40
|
+
from alpaca.common.models import BaseModel
|
41
|
+
from alpaca.common.requests import NonEmptyRequest
|
42
|
+
from alpaca.trading.enums import AccountStatus, ActivityType, AssetClass, OrderType
|
43
|
+
from alpaca.trading.requests import LimitOrderRequest as BaseLimitOrderRequest
|
44
|
+
from alpaca.trading.requests import MarketOrderRequest as BaseMarketOrderRequest
|
45
|
+
from alpaca.trading.requests import OrderRequest as BaseOrderRequest
|
46
|
+
from alpaca.trading.requests import StopLimitOrderRequest as BaseStopLimitOrderRequest
|
47
|
+
from alpaca.trading.requests import StopOrderRequest as BaseStopOrderRequest
|
48
|
+
from alpaca.trading.requests import (
|
49
|
+
TrailingStopOrderRequest as BaseTrailingStopOrderRequest,
|
50
|
+
)
|
51
|
+
|
52
|
+
# ############################## Accounts ################################# #
|
53
|
+
|
54
|
+
|
55
|
+
class UploadW8BenDocumentRequest(NonEmptyRequest):
|
56
|
+
"""
|
57
|
+
Attributes:
|
58
|
+
content (Optional[str]): A string containing Base64 encoded data to upload. Must be set if `content_data` is not
|
59
|
+
set.
|
60
|
+
content_data (Optional[W8BenDocument]): The data representing a W8BEN document in field form. Must be set if
|
61
|
+
`content` is not set.
|
62
|
+
mime_type (UploadDocumentMimeType): The mime type of the data in `content`, or if using `content_data` must be
|
63
|
+
UploadDocumentMimeType.JSON. If `content_data` is set this will default to JSON
|
64
|
+
"""
|
65
|
+
|
66
|
+
# These 2 are purposely undocumented as they should be here for NonEmptyRequest but they shouldn't be touched or
|
67
|
+
# set by users since they always need to be set values
|
68
|
+
document_type: DocumentType
|
69
|
+
document_sub_type: UploadDocumentSubType
|
70
|
+
|
71
|
+
content: Optional[str] = None
|
72
|
+
content_data: Optional[W8BenDocument] = None
|
73
|
+
mime_type: UploadDocumentMimeType
|
74
|
+
|
75
|
+
def __init__(self, **data) -> None:
|
76
|
+
# Always set these to their expected values
|
77
|
+
data["document_type"] = DocumentType.W8BEN
|
78
|
+
data["document_sub_type"] = UploadDocumentSubType.FORM_W8_BEN
|
79
|
+
|
80
|
+
if (
|
81
|
+
"mime_type" not in data
|
82
|
+
and "content_data" in data
|
83
|
+
and data["content_data"] is not None
|
84
|
+
):
|
85
|
+
data["mime_type"] = UploadDocumentMimeType.JSON
|
86
|
+
|
87
|
+
super().__init__(**data)
|
88
|
+
|
89
|
+
@model_validator(mode="before")
|
90
|
+
def root_validator(cls, values: dict) -> dict:
|
91
|
+
content_is_none = values.get("content", None) is None
|
92
|
+
content_data_is_none = values.get("content_data", None) is None
|
93
|
+
|
94
|
+
if content_is_none and content_data_is_none:
|
95
|
+
raise ValueError(
|
96
|
+
"You must specify one of either the `content` or `content_data` fields"
|
97
|
+
)
|
98
|
+
|
99
|
+
if not content_is_none and not content_data_is_none:
|
100
|
+
raise ValueError(
|
101
|
+
"You can only specify one of either the `content` or `content_data` fields"
|
102
|
+
)
|
103
|
+
|
104
|
+
if values["document_type"] != DocumentType.W8BEN:
|
105
|
+
raise ValueError("document_type must be W8BEN.")
|
106
|
+
|
107
|
+
if values["document_sub_type"] != UploadDocumentSubType.FORM_W8_BEN:
|
108
|
+
raise ValueError("document_sub_type must be FORM_W8_BEN.")
|
109
|
+
|
110
|
+
if (
|
111
|
+
not content_data_is_none
|
112
|
+
and values["mime_type"] != UploadDocumentMimeType.JSON
|
113
|
+
):
|
114
|
+
raise ValueError("If `content_data` is set then `mime_type` must be JSON")
|
115
|
+
|
116
|
+
return values
|
117
|
+
|
118
|
+
|
119
|
+
class CreateAccountRequest(NonEmptyRequest):
|
120
|
+
"""Class used to format data necessary for making a request to create a brokerage account
|
121
|
+
|
122
|
+
Attributes:
|
123
|
+
contact (Contact): The contact details for the account holder
|
124
|
+
identity (Identity): The identity details for the account holder
|
125
|
+
disclosures (Disclosures): The account holder's political disclosures
|
126
|
+
agreements (List[Agreement]): The agreements the account holder has signed
|
127
|
+
documents (List[Union[AccountDocument, UploadW8BenDocumentRequest]]): The documents the account holder has submitted
|
128
|
+
trusted_contact (TrustedContact): The account holder's trusted contact details
|
129
|
+
"""
|
130
|
+
|
131
|
+
contact: Contact
|
132
|
+
identity: Identity
|
133
|
+
disclosures: Disclosures
|
134
|
+
agreements: List[Agreement]
|
135
|
+
documents: Optional[List[Union[AccountDocument, UploadW8BenDocumentRequest]]] = None
|
136
|
+
trusted_contact: Optional[TrustedContact] = None
|
137
|
+
currency: Optional[SupportedCurrencies] = None # None = USD
|
138
|
+
enabled_assets: Optional[List[AssetClass]] = None # None = Default to server
|
139
|
+
|
140
|
+
@model_validator(mode="before")
|
141
|
+
def validate_parameters_only_optional_in_response(cls, values: dict) -> dict:
|
142
|
+
"""
|
143
|
+
Validate parameters that are optional in the response but not in the request.
|
144
|
+
"""
|
145
|
+
nullable_fields_by_model = {
|
146
|
+
"contact": "phone_number",
|
147
|
+
"identity": "date_of_birth",
|
148
|
+
"disclosures": "is_control_person",
|
149
|
+
"disclosures": "is_affiliated_exchange_or_finra",
|
150
|
+
"disclosures": "is_politically_exposed",
|
151
|
+
}
|
152
|
+
for model, field in nullable_fields_by_model.items():
|
153
|
+
if dict(values[model]).get(field, None) is None:
|
154
|
+
raise ValueError(f"{field} is required to create a new account.")
|
155
|
+
return values
|
156
|
+
|
157
|
+
|
158
|
+
class UpdatableContact(Contact):
|
159
|
+
"""
|
160
|
+
An extended version of Contact that has all fields as optional, so you don't need to specify all fields if you only
|
161
|
+
want to update a subset of them.
|
162
|
+
|
163
|
+
Attributes:
|
164
|
+
email_address (Optional[str]): The user's email address
|
165
|
+
phone_number (Optional[str]): The user's phone number. It should include the country code.
|
166
|
+
street_address (Optional[List[str]]): The user's street address lines.
|
167
|
+
unit (Optional[str]): The user's apartment unit, if any.
|
168
|
+
city (Optional[str]): The city the user resides in.
|
169
|
+
state (Optional[str]): The state the user resides in. This is required if country is 'USA'.
|
170
|
+
postal_code (Optional[str]): The user's postal
|
171
|
+
country (Optional[str]): The country the user resides in. 3 letter country code is permissible.
|
172
|
+
"""
|
173
|
+
|
174
|
+
# override the non-optional fields to now be optional
|
175
|
+
email_address: Optional[str] = None
|
176
|
+
phone_number: Optional[str] = None
|
177
|
+
street_address: Optional[List[str]] = None
|
178
|
+
city: Optional[str] = None
|
179
|
+
|
180
|
+
|
181
|
+
# We don't extend the Identity model because we have to remove fields, not all of them are updatable
|
182
|
+
class UpdatableIdentity(NonEmptyRequest):
|
183
|
+
"""
|
184
|
+
This class is a subset version of Identity. Currently, not all fields on accounts are modifiable so this class
|
185
|
+
represents which ones are modifiable on the `identity` field of an account when making an
|
186
|
+
BrokerClient::update_account call.
|
187
|
+
|
188
|
+
Also has all fields as optional, so you don't need to specify all fields if you only want to update a subset
|
189
|
+
|
190
|
+
Attributes:
|
191
|
+
given_name (Optional[str]): The user's first name
|
192
|
+
middle_name (Optional[str]): The user's middle name, if any
|
193
|
+
family_name (Optional[str]): The user's last name
|
194
|
+
tax_id (Optional[str]): The user's country specific tax id, required if tax_id_type is provided
|
195
|
+
tax_id_type (Optional[TaxIdType]): The tax_id_type for the tax_id provided, required if tax_id provided
|
196
|
+
country_of_citizenship (Optional[str]): The country the user is a citizen
|
197
|
+
country_of_birth (Optional[str]): The country the user was born
|
198
|
+
country_of_tax_residence (Optional[str]): The country the user files taxes
|
199
|
+
visa_type (Optional[VisaType]): Only used to collect visa types for users residing in the USA.
|
200
|
+
visa_expiration_date (Optional[str]): The date of expiration for visa, Required if visa_type is set.
|
201
|
+
date_of_departure_from_usa (Optional[str]): Required if visa_type = B1 or B2
|
202
|
+
permanent_resident (Optional[bool]): Only used to collect permanent residence status in the USA.
|
203
|
+
funding_source (Optional[List[FundingSource]]): How the user will fund their account
|
204
|
+
annual_income_min (Optional[float]): The minimum of the user's income range
|
205
|
+
annual_income_max (Optional[float]): The maximum of the user's income range
|
206
|
+
liquid_net_worth_min (Optional[float]): The minimum of the user's liquid net worth range
|
207
|
+
liquid_net_worth_max (Optional[float]): The maximum of the user's liquid net worth range
|
208
|
+
total_net_worth_min (Optional[float]): The minimum of the user's total net worth range
|
209
|
+
total_net_worth_max (Optional[float]): The maximum of the user's total net worth range
|
210
|
+
"""
|
211
|
+
|
212
|
+
given_name: Optional[str] = None
|
213
|
+
middle_name: Optional[str] = None
|
214
|
+
family_name: Optional[str] = None
|
215
|
+
visa_type: Optional[VisaType] = None
|
216
|
+
visa_expiration_date: Optional[str] = None
|
217
|
+
date_of_departure_from_usa: Optional[str] = None
|
218
|
+
permanent_resident: Optional[bool] = None
|
219
|
+
funding_source: Optional[List[FundingSource]] = None
|
220
|
+
annual_income_min: Optional[float] = None
|
221
|
+
annual_income_max: Optional[float] = None
|
222
|
+
liquid_net_worth_min: Optional[float] = None
|
223
|
+
liquid_net_worth_max: Optional[float] = None
|
224
|
+
total_net_worth_min: Optional[float] = None
|
225
|
+
total_net_worth_max: Optional[float] = None
|
226
|
+
|
227
|
+
|
228
|
+
class UpdatableDisclosures(Disclosures):
|
229
|
+
"""
|
230
|
+
An extended version of Disclosures that has all fields as optional, so you don't need to specify all fields if you
|
231
|
+
only want to update a subset of them.
|
232
|
+
|
233
|
+
Attributes:
|
234
|
+
is_control_person (Optional[bool]): Whether user holds a controlling position in a publicly traded company
|
235
|
+
is_affiliated_exchange_or_finra (Optional[bool]): If user is affiliated with any exchanges or FINRA
|
236
|
+
is_politically_exposed (Optional[bool]): If user is politically exposed
|
237
|
+
immediate_family_exposed (Optional[bool]): If user’s immediate family member is either politically exposed or holds a control position.
|
238
|
+
employment_status (Optional[EmploymentStatus]): The employment status of the user
|
239
|
+
employer_name (Optional[str]): The user's employer's name, if any
|
240
|
+
employer_address (Optional[str]): The user's employer's address, if any
|
241
|
+
employment_position (Optional[str]): The user's employment position, if any
|
242
|
+
"""
|
243
|
+
|
244
|
+
is_control_person: Optional[bool] = None
|
245
|
+
is_affiliated_exchange_or_finra: Optional[bool] = None
|
246
|
+
is_politically_exposed: Optional[bool] = None
|
247
|
+
immediate_family_exposed: Optional[bool] = None
|
248
|
+
|
249
|
+
|
250
|
+
class UpdatableTrustedContact(TrustedContact):
|
251
|
+
"""
|
252
|
+
An extended version of TrustedContact that has all fields as optional, so you don't need to specify all fields if
|
253
|
+
you only want to update a subset of them.
|
254
|
+
|
255
|
+
Attributes:
|
256
|
+
given_name (Optional[str]): The first name of the user's trusted contact
|
257
|
+
family_name (Optional[str]): The last name of the user's trusted contact
|
258
|
+
email_address (Optional[str]): The email address of the user's trusted contact
|
259
|
+
phone_number (Optional[str]): The email address of the user's trusted contact
|
260
|
+
city (Optional[str]): The email address of the user's trusted contact
|
261
|
+
state (Optional[str]): The email address of the user's trusted contact
|
262
|
+
postal_code (Optional[str]): The email address of the user's trusted contact
|
263
|
+
country (Optional[str]): The email address of the user's trusted contact
|
264
|
+
"""
|
265
|
+
|
266
|
+
# only need to override these 2 as other fields were already optional
|
267
|
+
given_name: Optional[str] = None
|
268
|
+
family_name: Optional[str] = None
|
269
|
+
|
270
|
+
# override the parent and set a new root field_validator that just allows all
|
271
|
+
@model_validator(mode="before")
|
272
|
+
def root_validator(cls, values: dict) -> dict:
|
273
|
+
"""Override parent method to allow null contact info"""
|
274
|
+
return values
|
275
|
+
|
276
|
+
|
277
|
+
class UpdateAccountRequest(NonEmptyRequest):
|
278
|
+
"""
|
279
|
+
Represents the data allowed in a request to update an Account. Note not all fields of an account
|
280
|
+
are currently modifiable so this model uses models that represent the subset of modifiable fields.
|
281
|
+
|
282
|
+
Attributes:
|
283
|
+
contact (Optional[UpdatableContact]): Contact details to update to
|
284
|
+
identity (Optional[UpdatableIdentity]): Identity details to update to
|
285
|
+
disclosures (Optional[UpdatableDisclosures]): Disclosure details to update to
|
286
|
+
trusted_contact (Optional[UpdatableTrustedContact]): TrustedContact details to update to
|
287
|
+
"""
|
288
|
+
|
289
|
+
contact: Optional[UpdatableContact] = None
|
290
|
+
identity: Optional[UpdatableIdentity] = None
|
291
|
+
disclosures: Optional[UpdatableDisclosures] = None
|
292
|
+
trusted_contact: Optional[UpdatableTrustedContact] = None
|
293
|
+
|
294
|
+
|
295
|
+
class ListAccountsRequest(NonEmptyRequest):
|
296
|
+
"""
|
297
|
+
Represents the values you can specify when making a request to list accounts
|
298
|
+
|
299
|
+
Attributes:
|
300
|
+
query (Optional[str]): Pass space-delimited tokens. The response will contain accounts that match with each of
|
301
|
+
the tokens (logical AND). A match means the token is present in either the account’s associated account number,
|
302
|
+
phone number, name, or e-mail address (logical OR).
|
303
|
+
created_before (Optional[datetime]): Accounts that were created before this date
|
304
|
+
created_after (Optional[datetime]): Accounts that were created after this date
|
305
|
+
status (Optional[AccountStatus]): Accounts that have their status field as one of these
|
306
|
+
sort (Sort, optional): The chronological order of response based on the submission time. Defaults to DESC.
|
307
|
+
entities (Optional[List[AccountEntities]]): By default, this endpoint doesn't return all information for each
|
308
|
+
account to save space in the response. This field lets you specify what additional information you want to be
|
309
|
+
included on each account.
|
310
|
+
|
311
|
+
ie, specifying [IDENTITY, CONTACT] would ensure that each returned account has its `identity` and `contact`
|
312
|
+
fields filled out.
|
313
|
+
"""
|
314
|
+
|
315
|
+
query: Optional[str] = None
|
316
|
+
created_before: Optional[datetime] = None
|
317
|
+
created_after: Optional[datetime] = None
|
318
|
+
status: Optional[List[AccountStatus]] = None
|
319
|
+
sort: Sort
|
320
|
+
entities: Optional[List[AccountEntities]] = None
|
321
|
+
|
322
|
+
def __init__(self, *args, **kwargs):
|
323
|
+
# The api itself actually defaults to DESC, but this way our docs won't be incorrect if the api changes under us
|
324
|
+
if "sort" not in kwargs or kwargs["sort"] is None:
|
325
|
+
kwargs["sort"] = Sort.DESC
|
326
|
+
|
327
|
+
super().__init__(*args, **kwargs)
|
328
|
+
|
329
|
+
|
330
|
+
class GetAccountActivitiesRequest(NonEmptyRequest):
|
331
|
+
"""
|
332
|
+
Represents the filtering values you can specify when getting AccountActivities for an Account
|
333
|
+
|
334
|
+
**Notes on pagination and the `page_size` and `page_token` fields**.
|
335
|
+
|
336
|
+
The BrokerClient::get_account_activities function by default will automatically handle the pagination of results
|
337
|
+
for you to get all results at once. However, if you're requesting a very large amount of results this can use a
|
338
|
+
large amount of memory and time to gather all the results. If you instead want to handle
|
339
|
+
pagination yourself `page_size` and `page_token` are how you would handle this.
|
340
|
+
|
341
|
+
Say you put in a request with `page_size` set to 4, you'll only get 4 results back to get
|
342
|
+
the next "page" of results you would set `page_token` to be the `id` field of the last Activity returned in the
|
343
|
+
result set.
|
344
|
+
|
345
|
+
This gets more indepth if you start specifying the `sort` field as well. If specified with a direction of Sort.DESC,
|
346
|
+
for example, the results will end before the activity with the specified ID. However, specified with a direction of
|
347
|
+
Sort.ASC, results will begin with the activity immediately after the one specified.
|
348
|
+
|
349
|
+
Also, to note if `date` is not specified, the default and maximum `page_size` value is 100. If `date` is specified,
|
350
|
+
the default behavior is to return all results, and there is no maximum page size; page size is still supported in
|
351
|
+
this state though.
|
352
|
+
|
353
|
+
Please see https://alpaca.markets/docs/api-references/broker-api/accounts/account-activities/#retrieving-account-activities
|
354
|
+
for more information
|
355
|
+
|
356
|
+
Attributes:
|
357
|
+
account_id (Optional[Union[UUID, str]]): Specifies to filter to only activities for this Account
|
358
|
+
activity_types (Optional[List[ActivityType]]): A list of ActivityType's to filter results down to
|
359
|
+
date (Optional[datetime]): Filter to Activities only on this date.
|
360
|
+
until (Optional[datetime]): Filter to Activities before this date. Cannot be used if `date` is also specified.
|
361
|
+
after (Optional[datetime]): Filter to Activities after this date. Cannot be used if `date` is also specified.
|
362
|
+
direction (Optional[Sort]): Which direction to sort results in. Defaults to Sort.DESC
|
363
|
+
page_size (Optional[int]): The maximum number of entries to return in the response
|
364
|
+
page_token (Optional[Union[UUID, str]]): If you're not using the built-in pagination this field is what you
|
365
|
+
would use to mark the end of the results of your last page.
|
366
|
+
"""
|
367
|
+
|
368
|
+
account_id: Optional[Union[UUID, str]] = None
|
369
|
+
activity_types: Optional[List[ActivityType]] = None
|
370
|
+
date: Optional[datetime] = None
|
371
|
+
until: Optional[datetime] = None
|
372
|
+
after: Optional[datetime] = None
|
373
|
+
direction: Optional[Sort] = None
|
374
|
+
page_size: Optional[int] = None
|
375
|
+
page_token: Optional[Union[UUID, str]] = None
|
376
|
+
|
377
|
+
def __init__(self, *args, **kwargs):
|
378
|
+
if "account_id" in kwargs and type(kwargs["account_id"]) == str:
|
379
|
+
kwargs["account_id"] = UUID(kwargs["account_id"])
|
380
|
+
|
381
|
+
super().__init__(*args, **kwargs)
|
382
|
+
|
383
|
+
@model_validator(mode="before")
|
384
|
+
def root_validator(cls, values: dict) -> dict:
|
385
|
+
"""Verify that certain conflicting params aren't set"""
|
386
|
+
|
387
|
+
date_set = "date" in values and values["date"] is not None
|
388
|
+
after_set = "after" in values and values["after"] is not None
|
389
|
+
until_set = "until" in values and values["until"] is not None
|
390
|
+
|
391
|
+
if date_set and after_set:
|
392
|
+
raise ValueError("Cannot set date and after at the same time")
|
393
|
+
|
394
|
+
if date_set and until_set:
|
395
|
+
raise ValueError("Cannot set date and until at the same time")
|
396
|
+
|
397
|
+
return values
|
398
|
+
|
399
|
+
@field_serializer("activity_types")
|
400
|
+
def serialize_activity_types(self, activity_types: Optional[List[ActivityType]]):
|
401
|
+
if activity_types:
|
402
|
+
return ",".join([t.value for t in activity_types])
|
403
|
+
|
404
|
+
|
405
|
+
# ############################## Documents ################################# #
|
406
|
+
|
407
|
+
|
408
|
+
class GetTradeDocumentsRequest(NonEmptyRequest):
|
409
|
+
"""
|
410
|
+
Represents the various filters you can specify when making a call to get TradeDocuments for an Account
|
411
|
+
|
412
|
+
Attributes:
|
413
|
+
start (Optional[Union[date, str]]): Filter to TradeDocuments created after this Date. str values will attempt to
|
414
|
+
be upcast into date instances. Format must be in YYYY-MM-DD.
|
415
|
+
end (Optional[Union[date, str]]): Filter to TradeDocuments created before this Date. str values will attempt to
|
416
|
+
be upcast into date instances. Format must be in YYYY-MM-DD.
|
417
|
+
type (Optional[TradeDocumentType]): Filter to only these types of TradeDocuments
|
418
|
+
"""
|
419
|
+
|
420
|
+
start: Optional[Union[date, str]] = None
|
421
|
+
end: Optional[Union[date, str]] = None
|
422
|
+
type: Optional[TradeDocumentType] = None
|
423
|
+
|
424
|
+
def __init__(self, **data) -> None:
|
425
|
+
if "start" in data and isinstance(data["start"], str):
|
426
|
+
data["start"] = date.fromisoformat(data["start"])
|
427
|
+
|
428
|
+
if "end" in data and isinstance(data["end"], str):
|
429
|
+
data["end"] = date.fromisoformat(data["end"])
|
430
|
+
|
431
|
+
super().__init__(**data)
|
432
|
+
|
433
|
+
@model_validator(mode="before")
|
434
|
+
def root_validator(cls, values: dict) -> dict:
|
435
|
+
if (
|
436
|
+
"start" in values
|
437
|
+
and values["start"] is not None
|
438
|
+
and "end" in values
|
439
|
+
and values["end"] is not None
|
440
|
+
and values["start"] > values["end"]
|
441
|
+
):
|
442
|
+
raise ValueError("start must not be after end!!")
|
443
|
+
|
444
|
+
return values
|
445
|
+
|
446
|
+
|
447
|
+
class UploadDocumentRequest(NonEmptyRequest):
|
448
|
+
"""
|
449
|
+
Attributes:
|
450
|
+
document_type (DocumentType): The type of document you are uploading
|
451
|
+
document_sub_type (Optional[UploadDocumentSubType]): If supported for the corresponding `document_type` this
|
452
|
+
field allows you to specify a sub type to be even more specific.
|
453
|
+
content (str): A string containing Base64 encoded data to upload.
|
454
|
+
mime_type (UploadDocumentMimeType): The mime type of the data in `content`
|
455
|
+
"""
|
456
|
+
|
457
|
+
document_type: DocumentType
|
458
|
+
document_sub_type: Optional[UploadDocumentSubType] = None
|
459
|
+
content: str
|
460
|
+
mime_type: UploadDocumentMimeType
|
461
|
+
|
462
|
+
@model_validator(mode="before")
|
463
|
+
def root_validator(cls, values: dict) -> dict:
|
464
|
+
if values["document_type"] == DocumentType.W8BEN:
|
465
|
+
raise ValueError(
|
466
|
+
"Error please use the UploadW8BenDocument class for uploading W8BEN documents"
|
467
|
+
)
|
468
|
+
|
469
|
+
if values.get("document_sub_type", None) == UploadDocumentSubType.FORM_W8_BEN:
|
470
|
+
raise ValueError(
|
471
|
+
"Error please use the UploadW8BenDocument class for uploading W8BEN documents"
|
472
|
+
)
|
473
|
+
|
474
|
+
return values
|
475
|
+
|
476
|
+
|
477
|
+
# ############################## Banking and Transfers ################################# #
|
478
|
+
|
479
|
+
|
480
|
+
class CreateACHRelationshipRequest(NonEmptyRequest):
|
481
|
+
"""
|
482
|
+
Attributes:
|
483
|
+
account_owner_name (str): The name of the ACH account owner for the relationship that is being created.
|
484
|
+
bank_account_type (BankAccountType): Specifies the type of bank account for the ACH relationship that is being
|
485
|
+
created.
|
486
|
+
bank_account_number (str): The bank account number associated with the ACH relationship.
|
487
|
+
bank_routing_number (str): THe bank routing number associated with the ACH relationship.
|
488
|
+
nickname (Optional[str]): Optionally specify a nickname to assign to the created ACH relationship.
|
489
|
+
"""
|
490
|
+
|
491
|
+
account_owner_name: str
|
492
|
+
bank_account_type: BankAccountType
|
493
|
+
bank_account_number: str # TODO: Validate bank account number format.
|
494
|
+
bank_routing_number: str # TODO: Validate bank routing number format.
|
495
|
+
nickname: Optional[str] = None
|
496
|
+
|
497
|
+
|
498
|
+
class CreatePlaidRelationshipRequest(NonEmptyRequest):
|
499
|
+
"""
|
500
|
+
This request is made following the Plaid bank account link user flow.
|
501
|
+
|
502
|
+
Upon the user completing their connection with Plaid, a public token specific to the user is returned by Plaid. This
|
503
|
+
token is used to get an Alpaca processor token via Plaid's /processor/token/create endpoint, which is subsequently
|
504
|
+
used by this endpoint to transfer the user's Plaid information to Alpaca.
|
505
|
+
|
506
|
+
Attributes:
|
507
|
+
processor_token (str): The processor token that is specific to Alpaca and was returned by Plaid.
|
508
|
+
"""
|
509
|
+
|
510
|
+
processor_token: str
|
511
|
+
|
512
|
+
|
513
|
+
class CreateBankRequest(NonEmptyRequest):
|
514
|
+
"""
|
515
|
+
Attributes:
|
516
|
+
name (str): The name of the recipient bank.
|
517
|
+
bank_code_type (IdentifierType): Specifies the type of the bank (international or domestic). See
|
518
|
+
enums.IdentifierType for more details.
|
519
|
+
bank_code (str): The 9-digit ABA routing number (domestic) or bank identifier code (BIC, international).
|
520
|
+
account_number (str): The bank account number.
|
521
|
+
country (Optional[str]): The country of the bank, if and only if creating an international bank account
|
522
|
+
connection.
|
523
|
+
state_province (Optional[str]): The state/province of the bank, if and only if creating an international bank
|
524
|
+
account connection.
|
525
|
+
postal_code (Optional[str]): The postal code of the bank, if and only if creating an international bank account
|
526
|
+
connection.
|
527
|
+
city (Optional[str]): The city of the bank, if and only if creating an international bank account connection.
|
528
|
+
street_address (Optional[str]): The street address of the bank, if and only if creating an international bank
|
529
|
+
account connection.
|
530
|
+
"""
|
531
|
+
|
532
|
+
name: str
|
533
|
+
bank_code_type: IdentifierType
|
534
|
+
bank_code: str
|
535
|
+
account_number: str
|
536
|
+
country: Optional[str] = None
|
537
|
+
state_province: Optional[str] = None
|
538
|
+
postal_code: Optional[str] = None
|
539
|
+
city: Optional[str] = None
|
540
|
+
street_address: Optional[str] = None
|
541
|
+
|
542
|
+
@model_validator(mode="before")
|
543
|
+
def root_validator(cls, values: dict) -> dict:
|
544
|
+
if "bank_code_type" not in values:
|
545
|
+
# Bank code type was not valid, so a ValueError will be thrown regardless.
|
546
|
+
return values
|
547
|
+
|
548
|
+
international_parameters = [
|
549
|
+
"country",
|
550
|
+
"state_province",
|
551
|
+
"postal_code",
|
552
|
+
"city",
|
553
|
+
"street_address",
|
554
|
+
]
|
555
|
+
|
556
|
+
bank_code_type = values["bank_code_type"]
|
557
|
+
if bank_code_type == IdentifierType.ABA:
|
558
|
+
for international_param in international_parameters:
|
559
|
+
if (
|
560
|
+
international_param in values
|
561
|
+
and values[international_param] is not None
|
562
|
+
):
|
563
|
+
raise ValueError(
|
564
|
+
f"You may only specify the {international_param} for international bank accounts."
|
565
|
+
)
|
566
|
+
elif bank_code_type == IdentifierType.BIC:
|
567
|
+
for international_param in international_parameters:
|
568
|
+
if (
|
569
|
+
international_param not in values
|
570
|
+
or values[international_param] is None
|
571
|
+
):
|
572
|
+
raise ValueError(
|
573
|
+
f"You must specify the {international_param} for international bank accounts."
|
574
|
+
)
|
575
|
+
|
576
|
+
return values
|
577
|
+
|
578
|
+
|
579
|
+
class _CreateTransferRequest(NonEmptyRequest):
|
580
|
+
"""
|
581
|
+
Attributes:
|
582
|
+
amount (str): Amount of transfer, must be > 0. Any applicable fees will be deducted from this value.
|
583
|
+
direction (TransferDirection): Direction of the transfer.
|
584
|
+
timing (TransferTiming): Timing of the transfer.
|
585
|
+
fee_payment_method (Optional[FeePaymentMethod]): Determines how any applicable fees will be paid. Default value
|
586
|
+
is invoice.
|
587
|
+
"""
|
588
|
+
|
589
|
+
amount: str
|
590
|
+
direction: TransferDirection
|
591
|
+
timing: TransferTiming
|
592
|
+
fee_payment_method: Optional[FeePaymentMethod] = None
|
593
|
+
|
594
|
+
@field_validator("amount")
|
595
|
+
def amount_must_be_positive(cls, value: str) -> str:
|
596
|
+
if float(value) <= 0:
|
597
|
+
raise ValueError("You must provide an amount > 0.")
|
598
|
+
return value
|
599
|
+
|
600
|
+
|
601
|
+
class CreateACHTransferRequest(_CreateTransferRequest):
|
602
|
+
"""
|
603
|
+
Attributes:
|
604
|
+
transfer_type (TransferType): Type of the transfer.
|
605
|
+
relationship_id (Optional[UUID]): ID of the relationship to use for the transfer, required for ACH transfers.
|
606
|
+
"""
|
607
|
+
|
608
|
+
relationship_id: UUID
|
609
|
+
transfer_type: TransferType = TransferType.ACH
|
610
|
+
|
611
|
+
@field_validator("transfer_type")
|
612
|
+
def transfer_type_must_be_ach(cls, value: TransferType) -> TransferType:
|
613
|
+
if value != TransferType.ACH:
|
614
|
+
raise ValueError(
|
615
|
+
"Transfer type must be TransferType.ACH for ACH transfer requests."
|
616
|
+
)
|
617
|
+
return value
|
618
|
+
|
619
|
+
|
620
|
+
class CreateBankTransferRequest(_CreateTransferRequest):
|
621
|
+
"""
|
622
|
+
Attributes:
|
623
|
+
bank_id (UUID): ID of the bank to use for the transfer, required for wire transfers.
|
624
|
+
additional_information (Optional[str]): Additional wire transfer details.
|
625
|
+
"""
|
626
|
+
|
627
|
+
bank_id: UUID
|
628
|
+
transfer_type: TransferType = TransferType.WIRE
|
629
|
+
additional_information: Optional[str] = None
|
630
|
+
|
631
|
+
@field_validator("transfer_type")
|
632
|
+
def transfer_type_must_be_wire(cls, value: TransferType) -> TransferType:
|
633
|
+
if value != TransferType.WIRE:
|
634
|
+
raise ValueError(
|
635
|
+
"Transfer type must be TransferType.WIRE for bank transfer requests."
|
636
|
+
)
|
637
|
+
return value
|
638
|
+
|
639
|
+
|
640
|
+
class GetTransfersRequest(NonEmptyRequest):
|
641
|
+
"""
|
642
|
+
Attributes:
|
643
|
+
direction: Optionally filter for transfers of only a single TransferDirection.
|
644
|
+
"""
|
645
|
+
|
646
|
+
direction: Optional[TransferDirection] = None
|
647
|
+
limit: Optional[int] = None
|
648
|
+
offset: Optional[int] = None
|
649
|
+
|
650
|
+
|
651
|
+
# ############################## Orders ################################# #
|
652
|
+
|
653
|
+
|
654
|
+
class OrderRequest(BaseOrderRequest):
|
655
|
+
"""
|
656
|
+
See base alpaca.trading.requests.OrderRequest model for more information.
|
657
|
+
|
658
|
+
Attributes:
|
659
|
+
symbol (str): The symbol identifier for the asset being traded
|
660
|
+
qty (Optional[float]): The number of shares to trade. Fractional qty for stocks only with market orders.
|
661
|
+
notional (Optional[float]): The base currency value of the shares to trade. For stocks, only works with MarketOrders.
|
662
|
+
**Does not work with qty**.
|
663
|
+
side (OrderSide): Whether the order will buy or sell the asset.
|
664
|
+
type (OrderType): The execution logic type of the order (market, limit, etc).
|
665
|
+
time_in_force (TimeInForce): The expiration logic of the order.
|
666
|
+
extended_hours (Optional[float]): Whether the order can be executed during regular market hours.
|
667
|
+
client_order_id (Optional[float]): A string to identify which client submitted the order.
|
668
|
+
order_class (Optional[OrderClass]): The class of the order. Simple orders have no other legs.
|
669
|
+
take_profit (Optional[TakeProfitRequest]): For orders with multiple legs, an order to exit a profitable trade.
|
670
|
+
stop_loss (Optional[StopLossRequest]): For orders with multiple legs, an order to exit a losing trade.
|
671
|
+
commission (Optional[float]): The dollar value commission you want to charge the end user.
|
672
|
+
"""
|
673
|
+
|
674
|
+
commission: Optional[float] = None
|
675
|
+
currency: Optional[SupportedCurrencies] = None # None = USD
|
676
|
+
|
677
|
+
@model_validator(mode="before")
|
678
|
+
def order_type_must_be_market_for_lct(
|
679
|
+
cls, values: Dict[str, Any]
|
680
|
+
) -> Dict[str, Any]:
|
681
|
+
"""
|
682
|
+
Order type must always be market if currency is not USD.
|
683
|
+
See https://alpaca.markets/docs/broker/integration/lct/#submit-stock-trade
|
684
|
+
"""
|
685
|
+
if (
|
686
|
+
values.get("type") != OrderType.MARKET
|
687
|
+
and "currency" in values
|
688
|
+
and values.get("currency", SupportedCurrencies.USD)
|
689
|
+
!= SupportedCurrencies.USD
|
690
|
+
):
|
691
|
+
raise ValueError(
|
692
|
+
"Order type must be OrderType.MARKET if the order is in a local currency."
|
693
|
+
)
|
694
|
+
return values
|
695
|
+
|
696
|
+
|
697
|
+
class MarketOrderRequest(BaseMarketOrderRequest):
|
698
|
+
"""
|
699
|
+
See base alpaca.trading.requests.MarketOrderRequest model for more information.
|
700
|
+
|
701
|
+
Attributes:
|
702
|
+
symbol (str): The symbol identifier for the asset being traded
|
703
|
+
qty (Optional[float]): The number of shares to trade. Fractional qty for stocks only with market orders.
|
704
|
+
notional (Optional[float]): The base currency value of the shares to trade. For stocks, only works with MarketOrders.
|
705
|
+
**Does not work with qty**.
|
706
|
+
side (OrderSide): Whether the order will buy or sell the asset.
|
707
|
+
type (OrderType): The execution logic type of the order (market, limit, etc).
|
708
|
+
time_in_force (TimeInForce): The expiration logic of the order.
|
709
|
+
extended_hours (Optional[float]): Whether the order can be executed during regular market hours.
|
710
|
+
client_order_id (Optional[float]): A string to identify which client submitted the order.
|
711
|
+
order_class (Optional[OrderClass]): The class of the order. Simple orders have no other legs.
|
712
|
+
take_profit (Optional[TakeProfitRequest]): For orders with multiple legs, an order to exit a profitable trade.
|
713
|
+
stop_loss (Optional[StopLossRequest]): For orders with multiple legs, an order to exit a losing trade.
|
714
|
+
commission (Optional[float]): The dollar value commission you want to charge the end user.
|
715
|
+
"""
|
716
|
+
|
717
|
+
commission: Optional[float] = None
|
718
|
+
|
719
|
+
|
720
|
+
class LimitOrderRequest(BaseLimitOrderRequest):
|
721
|
+
"""
|
722
|
+
See base alpaca.trading.requests.LimitOrderRequest model for more information.
|
723
|
+
|
724
|
+
Attributes:
|
725
|
+
symbol (str): The symbol identifier for the asset being traded
|
726
|
+
qty (Optional[float]): The number of shares to trade. Fractional qty for stocks only with market orders.
|
727
|
+
notional (Optional[float]): The base currency value of the shares to trade. For stocks, only works with MarketOrders.
|
728
|
+
**Does not work with qty**.
|
729
|
+
side (OrderSide): Whether the order will buy or sell the asset.
|
730
|
+
type (OrderType): The execution logic type of the order (market, limit, etc).
|
731
|
+
time_in_force (TimeInForce): The expiration logic of the order.
|
732
|
+
extended_hours (Optional[float]): Whether the order can be executed during regular market hours.
|
733
|
+
client_order_id (Optional[float]): A string to identify which client submitted the order.
|
734
|
+
order_class (Optional[OrderClass]): The class of the order. Simple orders have no other legs.
|
735
|
+
take_profit (Optional[TakeProfitRequest]): For orders with multiple legs, an order to exit a profitable trade.
|
736
|
+
stop_loss (Optional[StopLossRequest]): For orders with multiple legs, an order to exit a losing trade.
|
737
|
+
limit_price (float): The worst fill price for a limit or stop limit order.
|
738
|
+
commission (Optional[float]): The dollar value commission you want to charge the end user.
|
739
|
+
"""
|
740
|
+
|
741
|
+
commission: Optional[float] = None
|
742
|
+
|
743
|
+
|
744
|
+
class StopOrderRequest(BaseStopOrderRequest):
|
745
|
+
"""
|
746
|
+
See base alpaca.trading.requests.StopOrderRequest model for more information.
|
747
|
+
|
748
|
+
Attributes:
|
749
|
+
symbol (str): The symbol identifier for the asset being traded
|
750
|
+
qty (Optional[float]): The number of shares to trade. Fractional qty for stocks only with market orders.
|
751
|
+
notional (Optional[float]): The base currency value of the shares to trade. For stocks, only works with MarketOrders.
|
752
|
+
**Does not work with qty**.
|
753
|
+
side (OrderSide): Whether the order will buy or sell the asset.
|
754
|
+
type (OrderType): The execution logic type of the order (market, limit, etc).
|
755
|
+
time_in_force (TimeInForce): The expiration logic of the order.
|
756
|
+
extended_hours (Optional[float]): Whether the order can be executed during regular market hours.
|
757
|
+
client_order_id (Optional[float]): A string to identify which client submitted the order.
|
758
|
+
order_class (Optional[OrderClass]): The class of the order. Simple orders have no other legs.
|
759
|
+
take_profit (Optional[TakeProfitRequest]): For orders with multiple legs, an order to exit a profitable trade.
|
760
|
+
stop_loss (Optional[StopLossRequest]): For orders with multiple legs, an order to exit a losing trade.
|
761
|
+
stop_price (float): The price at which the stop order is converted to a market order or a stop limit
|
762
|
+
order is converted to a limit order.
|
763
|
+
commission (Optional[float]): The dollar value commission you want to charge the end user.
|
764
|
+
"""
|
765
|
+
|
766
|
+
commission: Optional[float] = None
|
767
|
+
|
768
|
+
|
769
|
+
class StopLimitOrderRequest(BaseStopLimitOrderRequest):
|
770
|
+
"""
|
771
|
+
See base alpaca.trading.requests.StopLimitOrderRequest model for more information.
|
772
|
+
|
773
|
+
Attributes:
|
774
|
+
symbol (str): The symbol identifier for the asset being traded
|
775
|
+
qty (Optional[float]): The number of shares to trade. Fractional qty for stocks only with market orders.
|
776
|
+
notional (Optional[float]): The base currency value of the shares to trade. For stocks, only works with MarketOrders.
|
777
|
+
**Does not work with qty**.
|
778
|
+
side (OrderSide): Whether the order will buy or sell the asset.
|
779
|
+
type (OrderType): The execution logic type of the order (market, limit, etc).
|
780
|
+
time_in_force (TimeInForce): The expiration logic of the order.
|
781
|
+
extended_hours (Optional[float]): Whether the order can be executed during regular market hours.
|
782
|
+
client_order_id (Optional[float]): A string to identify which client submitted the order.
|
783
|
+
order_class (Optional[OrderClass]): The class of the order. Simple orders have no other legs.
|
784
|
+
take_profit (Optional[TakeProfitRequest]): For orders with multiple legs, an order to exit a profitable trade.
|
785
|
+
stop_loss (Optional[StopLossRequest]): For orders with multiple legs, an order to exit a losing trade.
|
786
|
+
stop_price (float): The price at which the stop order is converted to a market order or a stop limit
|
787
|
+
order is converted to a limit order.
|
788
|
+
limit_price (float): The worst fill price for a limit or stop limit order.
|
789
|
+
commission (Optional[float]): The dollar value commission you want to charge the end user
|
790
|
+
"""
|
791
|
+
|
792
|
+
commission: Optional[float] = None
|
793
|
+
|
794
|
+
|
795
|
+
class TrailingStopOrderRequest(BaseTrailingStopOrderRequest):
|
796
|
+
"""
|
797
|
+
See base alpaca.trading.requests.TrailingStopOrderRequest model for more information.
|
798
|
+
|
799
|
+
Attributes:
|
800
|
+
symbol (str): The symbol identifier for the asset being traded
|
801
|
+
qty (Optional[float]): The number of shares to trade. Fractional qty for stocks only with market orders.
|
802
|
+
notional (Optional[float]): The base currency value of the shares to trade. For stocks, only works with MarketOrders.
|
803
|
+
**Does not work with qty**.
|
804
|
+
side (OrderSide): Whether the order will buy or sell the asset.
|
805
|
+
type (OrderType): The execution logic type of the order (market, limit, etc).
|
806
|
+
time_in_force (TimeInForce): The expiration logic of the order.
|
807
|
+
extended_hours (Optional[float]): Whether the order can be executed during regular market hours.
|
808
|
+
client_order_id (Optional[float]): A string to identify which client submitted the order.
|
809
|
+
order_class (Optional[OrderClass]): The class of the order. Simple orders have no other legs.
|
810
|
+
take_profit (Optional[TakeProfitRequest]): For orders with multiple legs, an order to exit a profitable trade.
|
811
|
+
stop_loss (Optional[StopLossRequest]): For orders with multiple legs, an order to exit a losing trade.
|
812
|
+
trail_price (Optional[float]): The absolute price difference by which the trailing stop will trail.
|
813
|
+
trail_percent (Optional[float]): The percent price difference by which the trailing stop will trail.
|
814
|
+
commission (Optional[float]): The dollar value commission you want to charge the end user.
|
815
|
+
"""
|
816
|
+
|
817
|
+
commission: Optional[float] = None
|
818
|
+
|
819
|
+
|
820
|
+
# ############################## Journals ################################# #
|
821
|
+
|
822
|
+
|
823
|
+
class CreateJournalRequest(NonEmptyRequest):
|
824
|
+
"""
|
825
|
+
Data for request to initiate a single journal.
|
826
|
+
|
827
|
+
Attributes:
|
828
|
+
to_account (UUID): The account ID that received the journal.
|
829
|
+
from_account (UUID): The account ID that initiates the journal.
|
830
|
+
entry_type (JournalEntryType): Whether the journal is a cash or security journal.
|
831
|
+
symbol (Optional[str]): For security journals, the symbol identifier of the security being journaled.
|
832
|
+
qty (Optional[float]): For security journals, the quantity of the security being journaled.
|
833
|
+
amount (Optional[float]): For cash journals, the total cash amount journaled in USD.
|
834
|
+
description (Optional[str]): Journal description. It can include fixtures for sandbox API.
|
835
|
+
transmitter_name (Optional[str]): For cash journals, travel rule related name info.
|
836
|
+
transmitter_account_number (Optional[str]): For cash journals, travel rule account number info.
|
837
|
+
transmitter_address (Optional[str]): For cash journals, travel rule related address info.
|
838
|
+
transmitter_financial_institution (Optional[str]): For cash journals, travel rule related institution info.
|
839
|
+
transmitter_timestamp (Optional[str]): For cash journals, travel rule related timestamp info.
|
840
|
+
"""
|
841
|
+
|
842
|
+
from_account: UUID
|
843
|
+
entry_type: JournalEntryType
|
844
|
+
to_account: UUID
|
845
|
+
amount: Optional[float] = None
|
846
|
+
symbol: Optional[str] = None
|
847
|
+
qty: Optional[float] = None
|
848
|
+
description: Optional[str] = None
|
849
|
+
transmitter_name: Optional[str] = None
|
850
|
+
transmitter_account_number: Optional[str] = None
|
851
|
+
transmitter_address: Optional[str] = None
|
852
|
+
transmitter_financial_institution: Optional[str] = None
|
853
|
+
transmitter_timestamp: Optional[str] = None
|
854
|
+
currency: Optional[SupportedCurrencies] = None # None = USD
|
855
|
+
|
856
|
+
@model_validator(mode="before")
|
857
|
+
def root_validator(cls, values: dict) -> dict:
|
858
|
+
entry_type = values.get("entry_type")
|
859
|
+
symbol = values.get("symbol")
|
860
|
+
qty = values.get("qty")
|
861
|
+
amount = values.get("amount")
|
862
|
+
|
863
|
+
# amount is for cash journals, symbol and qty are for security journals
|
864
|
+
# they are mutually exclusive
|
865
|
+
if entry_type is not None and entry_type == JournalEntryType.CASH:
|
866
|
+
if symbol or qty:
|
867
|
+
raise ValueError("Symbol and qty are reserved for security journals.")
|
868
|
+
|
869
|
+
if not amount:
|
870
|
+
raise ValueError("Cash journals must contain an amount to transfer.")
|
871
|
+
|
872
|
+
if entry_type is not None and entry_type == JournalEntryType.SECURITY:
|
873
|
+
if amount:
|
874
|
+
raise ValueError("Amount is reserved for cash journals.")
|
875
|
+
|
876
|
+
if not symbol or not qty:
|
877
|
+
raise ValueError(
|
878
|
+
"Security journals must contain a symbol and corresponding qty to transfer."
|
879
|
+
)
|
880
|
+
|
881
|
+
return values
|
882
|
+
|
883
|
+
|
884
|
+
class BatchJournalRequestEntry(NonEmptyRequest):
|
885
|
+
"""
|
886
|
+
Entry in batch journal request.
|
887
|
+
|
888
|
+
Attributes:
|
889
|
+
to_account (UUID): Account to fund in batch journal request.
|
890
|
+
amount (Union[str, float]): The cash amount in USD to fund by.
|
891
|
+
description (Optional[str]): Journal description.
|
892
|
+
transmitter_name (Optional[str]): For cash journals, travel rule related name info.
|
893
|
+
transmitter_account_number (Optional[str]): For cash journals, travel rule account number info.
|
894
|
+
transmitter_address (Optional[str]): For cash journals, travel rule related address info.
|
895
|
+
transmitter_financial_institution (Optional[str]): For cash journals, travel rule related institution info.
|
896
|
+
transmitter_timestamp (Optional[str]): For cash journals, travel rule related timestamp info.
|
897
|
+
"""
|
898
|
+
|
899
|
+
to_account: UUID
|
900
|
+
amount: Union[str, float]
|
901
|
+
description: Optional[str] = None
|
902
|
+
transmitter_name: Optional[str] = None
|
903
|
+
transmitter_account_number: Optional[str] = None
|
904
|
+
transmitter_address: Optional[str] = None
|
905
|
+
transmitter_financial_institution: Optional[str] = None
|
906
|
+
transmitter_timestamp: Optional[str] = None
|
907
|
+
|
908
|
+
|
909
|
+
class ReverseBatchJournalRequestEntry(NonEmptyRequest):
|
910
|
+
"""
|
911
|
+
Entry in reverse batch journal request.
|
912
|
+
|
913
|
+
Attributes:
|
914
|
+
from_account (UUID): Account from fund in batch journal request.
|
915
|
+
amount (Union[str, float]): The cash amount in USD to fund by.
|
916
|
+
description (Optional[str]): Journal description.
|
917
|
+
transmitter_name (Optional[str]): For cash journals, travel rule related name info.
|
918
|
+
transmitter_account_number (Optional[str]): For cash journals, travel rule account number info.
|
919
|
+
transmitter_address (Optional[str]): For cash journals, travel rule related address info.
|
920
|
+
transmitter_financial_institution (Optional[str]): For cash journals, travel rule related institution info.
|
921
|
+
transmitter_timestamp (Optional[str]): For cash journals, travel rule related timestamp info.
|
922
|
+
"""
|
923
|
+
|
924
|
+
from_account: UUID
|
925
|
+
amount: Union[str, float]
|
926
|
+
description: Optional[str] = None
|
927
|
+
transmitter_name: Optional[str] = None
|
928
|
+
transmitter_account_number: Optional[str] = None
|
929
|
+
transmitter_address: Optional[str] = None
|
930
|
+
transmitter_financial_institution: Optional[str] = None
|
931
|
+
transmitter_timestamp: Optional[str] = None
|
932
|
+
|
933
|
+
|
934
|
+
class CreateBatchJournalRequest(NonEmptyRequest):
|
935
|
+
"""
|
936
|
+
This model represents the fields you can specify when creating
|
937
|
+
a request of many Journals out of one account to many others at once.
|
938
|
+
|
939
|
+
Currently, batch journals are only enabled on cash journals.
|
940
|
+
|
941
|
+
Attributes:
|
942
|
+
entry_type (JournalEntryType): The type of journal transfer.
|
943
|
+
from_account (UUID): The originator of funds. Most likely is your Sweep Firm Account
|
944
|
+
entries (List[BatchJournalRequestEntry]): List of journals to execute.
|
945
|
+
"""
|
946
|
+
|
947
|
+
entry_type: JournalEntryType
|
948
|
+
from_account: UUID
|
949
|
+
entries: List[BatchJournalRequestEntry]
|
950
|
+
|
951
|
+
|
952
|
+
class CreateReverseBatchJournalRequest(NonEmptyRequest):
|
953
|
+
"""
|
954
|
+
This model represents the fields you can specify when creating
|
955
|
+
a request of many Journals into one account from many other accounts at once.
|
956
|
+
|
957
|
+
Currently, reverse batch journals are only enabled on cash journals.
|
958
|
+
|
959
|
+
Attributes:
|
960
|
+
entry_type (JournalEntryType): The type of journal transfer.
|
961
|
+
to_account (UUID): The destination of funds. Most likely is your Sweep Firm Account
|
962
|
+
entries (List[BatchJournalRequestEntry]): List of journals to execute.
|
963
|
+
"""
|
964
|
+
|
965
|
+
entry_type: JournalEntryType
|
966
|
+
to_account: UUID
|
967
|
+
entries: List[ReverseBatchJournalRequestEntry]
|
968
|
+
|
969
|
+
|
970
|
+
class GetJournalsRequest(NonEmptyRequest):
|
971
|
+
"""
|
972
|
+
This model represents the fields you can specify when querying from the list of all journals.
|
973
|
+
|
974
|
+
Attributes:
|
975
|
+
after (Optional[date]): Journal creation dates after this date.
|
976
|
+
before (Optional[date]): Journal creation dates before this date.
|
977
|
+
status (Optional[JournalStatus]): Only journals with this status.
|
978
|
+
entry_type (Optional[JournalEntryType]): Only journals with this entry type.
|
979
|
+
to_account (Optional[UUID]): Only journals to this account.
|
980
|
+
from_account (Optional[UUID]): Only journals from this account.
|
981
|
+
"""
|
982
|
+
|
983
|
+
after: Optional[date] = None
|
984
|
+
before: Optional[date] = None
|
985
|
+
status: Optional[JournalStatus] = None
|
986
|
+
entry_type: Optional[JournalEntryType] = None
|
987
|
+
to_account: Optional[UUID] = None
|
988
|
+
from_account: Optional[UUID] = None
|
989
|
+
|
990
|
+
|
991
|
+
class GetEventsRequest(NonEmptyRequest):
|
992
|
+
id: Optional[str] = None
|
993
|
+
since: Optional[Union[date, str]] = None
|
994
|
+
until: Optional[Union[date, str]] = None
|
995
|
+
since_id: Optional[int] = None
|
996
|
+
until_id: Optional[int] = None
|
997
|
+
|
998
|
+
|
999
|
+
# ############################## Rebalancing ################################# #
|
1000
|
+
|
1001
|
+
|
1002
|
+
class Weight(BaseModel):
|
1003
|
+
"""
|
1004
|
+
Weight model.
|
1005
|
+
|
1006
|
+
https://docs.alpaca.markets/reference/post-v1-rebalancing-portfolios
|
1007
|
+
"""
|
1008
|
+
|
1009
|
+
type: WeightType
|
1010
|
+
symbol: Optional[str] = None
|
1011
|
+
percent: float
|
1012
|
+
|
1013
|
+
@field_validator("percent")
|
1014
|
+
def percent_must_be_positive(cls, value: float) -> float:
|
1015
|
+
"""Validate and round the percent field to 2 decimal places."""
|
1016
|
+
if value <= 0:
|
1017
|
+
raise ValueError("You must provide an amount > 0.")
|
1018
|
+
return round(value, 2)
|
1019
|
+
|
1020
|
+
@model_validator(mode="before")
|
1021
|
+
def validator(cls, values: dict) -> dict:
|
1022
|
+
"""Verify that the symbol is provided when the weights type is asset."""
|
1023
|
+
if (
|
1024
|
+
values["type"] == WeightType.ASSET.value
|
1025
|
+
and values.get("symbol", None) is None
|
1026
|
+
):
|
1027
|
+
raise ValueError
|
1028
|
+
return values
|
1029
|
+
|
1030
|
+
|
1031
|
+
class RebalancingConditions(BaseModel):
|
1032
|
+
"""
|
1033
|
+
Rebalancing conditions model.
|
1034
|
+
|
1035
|
+
https://docs.alpaca.markets/reference/post-v1-rebalancing-portfolios
|
1036
|
+
"""
|
1037
|
+
|
1038
|
+
type: RebalancingConditionsType
|
1039
|
+
sub_type: Union[DriftBandSubType, CalendarSubType]
|
1040
|
+
percent: Optional[float] = None
|
1041
|
+
day: Optional[str] = None
|
1042
|
+
|
1043
|
+
|
1044
|
+
class CreatePortfolioRequest(NonEmptyRequest):
|
1045
|
+
"""
|
1046
|
+
Portfolio request model.
|
1047
|
+
|
1048
|
+
https://docs.alpaca.markets/reference/post-v1-rebalancing-portfolios
|
1049
|
+
"""
|
1050
|
+
|
1051
|
+
name: str
|
1052
|
+
description: str
|
1053
|
+
weights: List[Weight]
|
1054
|
+
cooldown_days: int
|
1055
|
+
rebalance_conditions: Optional[List[RebalancingConditions]] = None
|
1056
|
+
|
1057
|
+
|
1058
|
+
class UpdatePortfolioRequest(NonEmptyRequest):
|
1059
|
+
"""
|
1060
|
+
Portfolio request update model.
|
1061
|
+
|
1062
|
+
https://docs.alpaca.markets/reference/patch-v1-rebalancing-portfolios-portfolio_id-1
|
1063
|
+
"""
|
1064
|
+
|
1065
|
+
name: Optional[str] = None
|
1066
|
+
description: Optional[str] = None
|
1067
|
+
weights: Optional[List[Weight]] = None
|
1068
|
+
cooldown_days: Optional[int] = None
|
1069
|
+
rebalance_conditions: Optional[List[RebalancingConditions]] = None
|
1070
|
+
|
1071
|
+
|
1072
|
+
class GetPortfoliosRequest(NonEmptyRequest):
|
1073
|
+
"""
|
1074
|
+
Get portfolios request query parameters.
|
1075
|
+
|
1076
|
+
https://docs.alpaca.markets/reference/get-v1-rebalancing-portfolios
|
1077
|
+
"""
|
1078
|
+
|
1079
|
+
name: Optional[str] = None
|
1080
|
+
description: Optional[str] = None
|
1081
|
+
symbol: Optional[str] = None
|
1082
|
+
portfolio_id: Optional[UUID] = None
|
1083
|
+
status: Optional[PortfolioStatus] = None
|
1084
|
+
|
1085
|
+
|
1086
|
+
class CreateSubscriptionRequest(NonEmptyRequest):
|
1087
|
+
"""
|
1088
|
+
Subscription request model.
|
1089
|
+
|
1090
|
+
https://docs.alpaca.markets/reference/post-v1-rebalancing-subscriptions-1
|
1091
|
+
"""
|
1092
|
+
|
1093
|
+
account_id: UUID
|
1094
|
+
portfolio_id: UUID
|
1095
|
+
|
1096
|
+
|
1097
|
+
class GetSubscriptionsRequest(NonEmptyRequest):
|
1098
|
+
"""
|
1099
|
+
Get subscriptions request query parameters.
|
1100
|
+
|
1101
|
+
https://docs.alpaca.markets/reference/get-v1-rebalancing-subscriptions-1
|
1102
|
+
"""
|
1103
|
+
|
1104
|
+
account_id: Optional[UUID] = None
|
1105
|
+
portfolio_id: Optional[UUID] = None
|
1106
|
+
limit: Optional[int] = None
|
1107
|
+
page_token: Optional[str] = None
|
1108
|
+
|
1109
|
+
|
1110
|
+
class CreateRunRequest(NonEmptyRequest):
|
1111
|
+
"""
|
1112
|
+
Manually creates a rebalancing run.
|
1113
|
+
|
1114
|
+
https://docs.alpaca.markets/reference/post-v1-rebalancing-runs
|
1115
|
+
"""
|
1116
|
+
|
1117
|
+
account_id: UUID
|
1118
|
+
type: RunType
|
1119
|
+
weights: List[Weight]
|
1120
|
+
|
1121
|
+
|
1122
|
+
class GetRunsRequest(NonEmptyRequest):
|
1123
|
+
"""
|
1124
|
+
Get runs request query parameters.
|
1125
|
+
|
1126
|
+
https://docs.alpaca.markets/reference/get-v1-rebalancing-runs
|
1127
|
+
"""
|
1128
|
+
|
1129
|
+
account_id: Optional[UUID] = None
|
1130
|
+
type: Optional[RunType] = None
|
1131
|
+
limit: Optional[int] = None
|
1132
|
+
|
1133
|
+
|
1134
|
+
class CreateOptionExerciseRequest(NonEmptyRequest):
|
1135
|
+
commission: Optional[float] = None
|