1
1
import logging
2
+ from datetime import datetime
2
3
from typing import Union
3
4
4
5
import dateutil .parser
@@ -31,16 +32,14 @@ def to_decimal_str(price: float, round_down: bool) -> str:
31
32
return spstr
32
33
33
34
34
- def get_request_result (
35
- req : OAuth1Session .request , empty_json : dict , resp_format : str = "xml"
36
- ) -> dict :
35
+ def get_request_result (req : OAuth1Session .request , resp_format : str = "xml" ) -> dict :
37
36
LOGGER .debug (req .text )
38
37
39
38
if resp_format == "json" :
40
39
if req .text .strip () == "" :
41
40
# otherwise, when ETrade server return empty string, we got this error:
42
41
# simplejson.errors.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
43
- req_output = empty_json # empty json object
42
+ req_output = {}
44
43
else :
45
44
req_output = req .json ()
46
45
else :
@@ -50,8 +49,7 @@ def get_request_result(
50
49
raise Exception (
51
50
f'Etrade API Error - Code: { req_output ["Error" ]["code" ]} , Msg: { req_output ["Error" ]["message" ]} '
52
51
)
53
- else :
54
- return req_output
52
+ return req_output
55
53
56
54
57
55
# return Etrade internal option symbol: e.g. "PLTR--220218P00023000" ref:_test_option_symbol()
@@ -96,7 +94,7 @@ class ETradeOrder(object):
96
94
:param resource_owner_key: Resource key from :class:`pyetrade.authorization.ETradeOAuth`
97
95
:type resource_owner_key: str, required
98
96
:param resource_owner_secret: Resource secret from
99
- :class:`pyetrade.authorization.ETradeOAuth`
97
+ :class: `pyetrade.authorization.ETradeOAuth`
100
98
:type resource_owner_secret: str, required
101
99
:param dev: Defines Sandbox (True) or Live (False) ETrade, defaults to True
102
100
:type dev: bool, optional
@@ -126,30 +124,135 @@ def __init__(
126
124
)
127
125
128
126
def list_orders (
129
- self , account_id_key : str , resp_format : str = "json" , ** kwargs
127
+ self ,
128
+ account_id_key : str ,
129
+ marker : str = None ,
130
+ count : int = 25 ,
131
+ status : str = None ,
132
+ from_date : datetime = None ,
133
+ to_date : datetime = None ,
134
+ symbols : list [str ] = None ,
135
+ security_type : str = None ,
136
+ transaction_type : str = None ,
137
+ market_session : str = "REGULAR" ,
138
+ resp_format : str = "json" ,
130
139
) -> dict :
131
140
""":description: Lists orders for a specific account ID Key
132
141
133
142
:param account_id_key: AccountIDKey from :class:`pyetrade.accounts.ETradeAccounts.list_accounts`
134
143
:type account_id_key: str, required
135
- :param resp_format: Desired Response format, defaults to xml
144
+ :param marker: Specifies the desired starting point of the set of items to return (defaults to None)
145
+ :type marker: str, optional
146
+ :param count: Number of transactions to return, defaults to 25 (max 100)
147
+ :type count: int, optional
148
+ :param status: Order status (defaults to None)
149
+ :type status: str, optional
150
+ :status values:
151
+ * OPEN
152
+ * EXECUTED
153
+ * CANCELLED
154
+ * INDIVIDUAL_FILLS
155
+ * CANCEL_REQUESTED
156
+ * EXPIRED
157
+ * REJECTED
158
+ :param from_date: The earliest date to include in the date range (history is available for two years).
159
+ Both fromDate and toDate should be used together, toDate should be greater than fromDate.
160
+ (defaults to None)
161
+ :type from_date: datetime obj, optional
162
+ :param to_date: The latest date to include in the date range (history is available for two years).
163
+ Both fromDate and toDate should be used together, toDate should be greater than fromDate.
164
+ (defaults to None)
165
+ :type to_date: datetime obj, optional
166
+ :param symbols: The market symbol(s) for the security being bought or sold. (defaults to None, Max 25 symbols)
167
+ :type symbols: list[str], optional
168
+ :param security_type: The security type (defaults to None - Returns all types)
169
+ :type security_type: str, optional
170
+ :security_type values:
171
+ * EQ
172
+ * OPTN
173
+ * MF
174
+ * MMF
175
+ :param transaction_type: Type of transaction (defaults to None - Returns all types)
176
+ :type transaction_type: str, optional
177
+ :transaction_type values:
178
+ * ATNM
179
+ * BUY
180
+ * SELL
181
+ * SELL_SHORT
182
+ * BUY_TO_COVER
183
+ * MF_EXCHANGE
184
+ :param market_session: The market session, defaults to REGULAR
185
+ :type market_session: str, optional
186
+ :market_session values:
187
+ * REGULAR
188
+ * EXTENDED
189
+ :param resp_format: Desired Response format, defaults to json
136
190
:type resp_format: str, optional
137
- :param kwargs: Parameters for api. Refer to EtradeRef for options
138
- :type kwargs: ``**kwargs``, optional
139
191
:return: List of orders for an account
140
192
:rtype: ``xml`` or ``json`` based on ``resp_format``
141
193
:EtradeRef: https://apisb.etrade.com/docs/api/order/api-order-v1.html
142
194
"""
143
195
144
- api_url = f"{ self .base_url } /{ account_id_key } /orders"
196
+ if symbols and len (symbols ) >= 26 :
197
+ LOGGER .warning (
198
+ "list_orders asked for %d requests; only first 25 returned"
199
+ % len (symbols )
200
+ )
145
201
146
- if resp_format == "json" :
147
- api_url += ".json"
202
+ api_url = f"{ self .base_url } /{ account_id_key } /orders{ '.json' if resp_format == 'json' else '' } "
203
+ LOGGER .debug (api_url )
204
+
205
+ if count >= 101 :
206
+ LOGGER .debug (
207
+ f"Count { count } is greater than the max allowable value (100), using 100"
208
+ )
209
+ count = 100
210
+
211
+ payload = {
212
+ "marker" : marker ,
213
+ "count" : count ,
214
+ "status" : status ,
215
+ "fromDate" : from_date .date ().strftime ("%m%d%Y" ) if from_date else from_date ,
216
+ "toDate" : to_date .date ().strftime ("%m%d%Y" ) if to_date else to_date ,
217
+ "symbol" : "," .join ([sym for sym in symbols [:25 ]]) if symbols else symbols ,
218
+ "securityType" : security_type ,
219
+ "transactionType" : transaction_type ,
220
+ "marketSession" : market_session ,
221
+ }
222
+
223
+ req = self .session .get (api_url , params = payload , timeout = self .timeout )
224
+ req .raise_for_status ()
225
+
226
+ LOGGER .debug (req .text )
227
+
228
+ return get_request_result (req , resp_format )
229
+
230
+ def list_order_details (
231
+ self , account_id_key : str , order_id : int , resp_format : str = "json"
232
+ ):
233
+ """
234
+ :description: Lists order details of a specific account ID Key and order ID
235
+
236
+ :param account_id_key: AccountIDKey from :class:`pyetrade.accounts.ETradeAccounts.list_accounts`
237
+ :type account_id_key: str, required
238
+ :param order_id: Order ID of placed order, order IDs can be retrieved from calling the list_orders() function
239
+ :type account_id_key: int, required
240
+ :param resp_format: Desired Response format, defaults to json
241
+ :type resp_format: str, optional
242
+ :return: List of orders for an account
243
+ :rtype: ``xml`` or ``json`` based on ``resp_format``
244
+ :EtradeRef: https://apisb.etrade.com/docs/api/order/api-order-v1.html
245
+ """
148
246
247
+ api_url = f"{ self .base_url } /{ account_id_key } /orders/{ order_id } { '.json' if resp_format == 'json' else '' } "
149
248
LOGGER .debug (api_url )
150
- req = self .session .get (api_url , params = kwargs , timeout = self .timeout )
151
249
152
- return get_request_result (req , {}, resp_format )
250
+ req = self .session .get (api_url )
251
+ req .raise_for_status ()
252
+
253
+ LOGGER .debug (req .text )
254
+
255
+ return xmltodict .parse (req .text ) if resp_format .lower () == "xml" else req .json ()
153
256
154
257
def find_option_orders (
155
258
self ,
@@ -327,7 +430,7 @@ def perform_request(
327
430
LOGGER .debug ("xml payload: %s" , payload )
328
431
req = method (api_url , data = payload , headers = headers , timeout = self .timeout )
329
432
330
- return get_request_result (req , {}, resp_format )
433
+ return get_request_result (req , resp_format )
331
434
332
435
def preview_equity_order (self , ** kwargs ) -> dict :
333
436
"""API is used to submit an order request for preview before placing it
@@ -496,6 +599,7 @@ def place_equity_order(self, **kwargs) -> dict:
496
599
"No previewId given, previewing before placing order "
497
600
"because of an Etrade bug as of 1/1/2019"
498
601
)
602
+
499
603
preview = self .preview_equity_order (** kwargs )
500
604
kwargs ["previewId" ] = preview ["PreviewOrderResponse" ]["PreviewIds" ][
501
605
"previewId"
0 commit comments