Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need help: preview request works but cannot place order #23

Closed
yangliu2 opened this issue May 30, 2020 · 44 comments
Closed

Need help: preview request works but cannot place order #23

yangliu2 opened this issue May 30, 2020 · 44 comments

Comments

@yangliu2
Copy link

First, I tried using your pyetrade package but not be able to place order.
Then I tried to use the Etrade provided sample code, but I got this error.

{'code': 101, 'message': '{com.etrade.api.v1.order.InvalidPreviewId}'

The second time I tried though, I got a different error.
{'code': 101, 'message': 'For your protection, we have timed out your original order request. If you would like to place this order, please resubmit it now.'}

Payload I sent is like this:

                   <PlaceOrderRequest>
                       <orderType>EQ</orderType>
                       <clientOrderId>5340619102</clientOrderId>
                       <previewIds>
                           <previewId>1627181131</previewId>
                           <cashMargin>CASH</cashMargin>
                       </previewIds>
                       <Order>
                           <allOrNone>false</allOrNone>
                           <priceType>LIMIT</priceType>
                           <orderTerm>IMMEDIATE_OR_CANCEL</orderTerm>
                           <marketSession>REGULAR</marketSession>
                           <stopPrice></stopPrice>
                           <limitPrice>100</limitPrice>
                           <Instrument>
                               <Product>
                                   <securityType>EQ</securityType>
                                   <symbol>GOOG</symbol>
                               </Product>
                               <orderAction>BUY</orderAction>
                               <quantityType>QUANTITY</quantityType>
                               <quantity>1</quantity>
                           </Instrument>
                       </Order>
                   </PlaceOrderRequest  >

I hate that Etrade makes me preview order before placing one, but I see that pyetrade also need to preview order before placing it. If you seen this before, any help is appreciated.
If you can give me some code example of how to use pyetrade to preview and place order, I would also much prefer doing it that way. I'm kinda reading challenged when reading documentations without code examples. Thanks!

@mkfrancis01
Copy link

Hello yangliu2. I am also having the same issue. Matter of fact I am not even able to execute the preview_equity_order() function. Getting "missing kwargs" error. Below are the details. Hope I am not hijacking your issue request but would you know what is causing this issue since I see you are able to execute preview_equity_order?

if not all(param in kwargs for param in mandatory):
--> 135 raise OrderException
136
137 if kwargs["priceType"] == "STOP" and "stopPrice" not in kwargs

Below is the kwargs dictionary I am using in SandBox environment
kwargs = {
"accountId":"6_Dpy0rmuQ9cu9IbTfvF2A",
"symbol":"AAPL",
"orderAction":"BUY",
"clientOrderId": random.randint(10000000,99999999),
"priceType":"MARKET",
"allOrNone": True,
"limitPrice":0,
"stopPrice":0,
}

@yangliu2
Copy link
Author

yangliu2 commented Jun 1, 2020

No problem man! I'm having the same error. I manually checked the required keys AND modified source code to check if "mandatory" have it. No luck. I don't know what's it's checking. This is where I went to Etrade sample code instead. Also remember trade sandbox have fixed response for preview and place order, so you need to make sure your placing order payload is the same as the preview response, not whatever payload you send it to them. Now I'm sticking on error 9999 for placing order.

@mkfrancis01
Copy link

Hello yangliu2

Thanks for your response. After trying a few things was able to make the preview order and place order to work. You simply have to make sure your kwargs dictionary is in the order in the mandatory list in the check order() function.

mandatory = ["accountId","symbol","orderAction","clientOrderId","priceType","quantity","orderTerm","marketSession"]

Ofcourse remember to use ** before kwargs in the paranthesis to show its a dictionary.

All this was in dev environment. I haven't tried actual prod environment..

Let me know how it goes.

@mkfrancis01
Copy link

mkfrancis01 commented Jun 4, 2020

Test these kwarg keys values,

kwargs = {"accountId":"6_Dpy0rmuQ9cu9IbTfvF2A",
"symbol":"GOGL",
"orderAction":"BUY",
"clientOrderId": random.randint(10000000,99999999),
"priceType":"MARKET",
"allOrNone": True,
"limitPrice":0,
"stopPrice":0,
"quantity":100,
"orderTerm":"GOOD_FOR_DAY",
"marketSession":"REGULAR"
}

Then after you instantiate the ETradeOrder class and create an object try calling the method below
myfirstorder.preview_equity_order(**kwargs)

@yangliu2
Copy link
Author

yangliu2 commented Jun 6, 2020

For some reason, if I use resp_format='json', then I get a 400 response error. Without the parameter, it seems to be fine. ¯\_(ツ)_/¯

@1rocketdude
Copy link
Collaborator

1rocketdude commented Jun 7, 2020 via email

@yangliu2 yangliu2 closed this as completed Jun 7, 2020
@gargv6880
Copy link

Hello yangliu2,

I am also facing exact same issue which you faced. Please let me know how you resolved this.

Thanks a lot.

@tommyhuangli
Copy link

Hi, @yangliu2 were you able to resolve this and place order successfully from python code?

@yangliu2
Copy link
Author

yangliu2 commented Mar 10, 2021 via email

@yui410122
Copy link

I am also trying to place an order with the ETrade API and got the following error:

{'code': 101, 'message': 'For your protection, we have timed out your original order request. If you would like to place this order, please resubmit it now.'}

the preview order seems to be working ok. If anyone figures how to successfully place an order please let me know, thanks!

@yui410122
Copy link

@yangliu2 could you please share the code ETrade support provides?

Thanks!

@tommyhuangli
Copy link

tommyhuangli commented Mar 21, 2021 via email

@yangliu2
Copy link
Author

Here is the code I got from Etrade. There weren't any formating, so you may have to retype.

Below is a simple method for place order. Make sure you copy the fields from preview order into your place order and you have to pass the previewId from the response from the preview order into the place order request or it will not work. Should you questions feel free to reply to this message.

def place_order(session, account, order):

"""

Calls place order API to submit an order request for confirmation after preview order is successfully called

:param session: authenticated session

:param account: information on selected account

:param order: list of instruments from previous orders

"""

URL for the API endpoint
url = "https://api.etrade.com/v1/accounts/" + account["accountIdKey"]+ "/orders/place.json"
Add parameters and header information
headers = {"Content-Type": "application/xml", "consumerKey": config["DEFAULT"]["CONSUMER_KEY"]}
Add payload for POST Request
payload = """
{0}
{1}
{2}
false
{3}
{4}
REGULAR
{5}
{6}
{7}
{8}
QUANTITY
{9}
"""
payload = payload.format(order["order_type"], order["client_order_id"], order["preview_id"],
order["price_type"], order["order_term"], order["limitPrice"],
order["security_type"], order["symbol"], order["order_action"], order["quantity"])
Make API call for POST request
response = session.post(url, header_auth=True, headers=headers, data=payload)
logger.debug("Request Header: %s", response.request.headers)
logger.debug("Request payload: %s", payload)
Handle and parse response
if response is not None and response.status_code == 200:
parsed = json.loads(response.text)
logger.debug("Response Body: %s", json.dumps(parsed, indent=4, sort_keys=True))
data = response.json()
print("\nOrder number #"
+ str(data["PlaceOrderResponse"]["OrderIds"][0]["orderId"])
+ " successfully placed.")
else:
Handle errors
data = response.json()
if 'Error' in data and 'message' in data["Error"]and data["Error"]["message"]is not None:
print("Error: " + data["Error"]["message"])
else:
print("Error: Preview Order API service error")

@yui410122
Copy link

Thanks @yangliu2 !

"Make sure you copy the fields from preview order into your place order and you have to pass the previewId from the response from the preview order into the place order request or it will not work."

This comment is really useful! I tried to include the previewID and now placing the order works!

Thanks a lot!

@jessecooper
Copy link
Owner

@yangliu2 Can you please open a PR with any changes that are needed in pyetrade to better support this?

@alanoatwork
Copy link

alanoatwork commented Aug 6, 2021

Thanks @yangliu2 !

"Make sure you copy the fields from preview order into your place order and you have to pass the previewId from the response from the preview order into the place order request or it will not work."

This comment is really useful! I tried to include the previewID and now placing the order works!

Thanks a lot!

I tried including the previewId in the order but I still get error 400 after the place_equity_order is called. Any clue why this isn't working??

   resp = orders.preview_equity_order(
        accountId=self.account_id,
        symbol="IBM",
        orderAction="BUY",
        clientOrderId="1",
        priceType="MARKET",
        quantity="1",
        orderTerm="GOOD_UNTIL_CANCEL",
        marketSession="REGULAR"
    )

    preview_id = resp['PreviewOrderResponse']['PreviewIds']['previewId'].get_cdata()

    resp = orders.place_equity_order(
        previewId=preview_id,
        accountId=self.account_id,
        symbol="IBM",
        orderAction="BUY",
        clientOrderId="1",
        priceType="MARKET",
        quantity="1",
        orderTerm="GOOD_UNTIL_CANCEL",
        marketSession="REGULAR"
    )

@AlgoMarket-Lab
Copy link

I've tried all of above solutions but None is working for me, place order keep responding same error and preview order working correctly

@Robert-Zacchigna
Copy link
Contributor

Robert-Zacchigna commented Dec 13, 2022

For anyone else who comes across this thread, i was unable to get the preview_equity_order and place_equity_order functions to work but i was able to come up with this workaround.

You basically need to make the request yourself using the underlying api endpoints instead of the functions. For these function to work, you need to have already setup pyetrades oauth (in my case its etrade_oauth) and order objs (in my case etrade_order)

import json
import xmltodict
import xml.etree.ElementTree as ET

from dicttoxml2 import dicttoxml

from pyetrade.order import ETradeOrder
from pyetrade import ETradeAccounts, ETradeOAuth

Function for making the post requests to the etrade api endpoints

def _post_etrade_req(etrade_oauth: ETradeOAuth, etrade_order: ETradeOrder, account_id_key: str,
                     order_params: dict, post_endpoint: str, print_payload: bool, print_response: bool) -> dict:

    order_payload = dicttoxml(order_params, root=False, attr_type=False).decode('UTF-8')

    if print_payload:
        pretty_xml = ET.XML(order_payload)
        ET.indent(pretty_xml)
        print(f'\n{post_endpoint.capitalize()} Order Request XML Payload:\n{"="*(27+len(post_endpoint))}\n'
              f'{ET.tostring(pretty_xml, encoding="unicode")}')

    headers = {"consumerKey": etrade_oauth.consumer_key, 'Content-Type': 'application/xml'}
    post_endpoint_url = f'https://api.etrade.com/v1/accounts/{account_id_key}/orders/{post_endpoint}'

    order_response = etrade_order.session.post(post_endpoint_url, headers=headers, data=order_payload)

    order_data = json.loads(json.dumps(xmltodict.parse(order_response.text)))
    
    if print_response:
        print(f'\n{post_endpoint.capitalize()} Order JSON Response:\n{"="*(21+len(post_endpoint))}\n'
              f'{json.dumps(order_data, indent=4)}')

    return order_data

My preview_shares_order function - this returns the preview_order_params and the preview_id from the preview_order_response:

# order_action_values = ['BUY', 'SELL', 'BUY_TO_COVER', 'SELL_SHORT']
# price_types_values = ['MARKET', 'LIMIT', 'STOP', 'STOP_LIMIT', 'MARKET_ON_CLOSE']
# market_session_values = ['REGULAR', 'EXTENDED']
# order_term_values = ['GOOD_UNTIL_CANCEL', 'GOOD_FOR_DAY', 'IMMEDIATE_OR_CANCEL', 'FILL_OR_KILL']
# routing_destinations_values = ['AUTO', 'ARCA', 'NSDQ', 'NYSE']

# Reference number generated by developer. Used to ensure duplicate order is not submitted.
# Value can be of 20 alphanmeric characters or less Must be unique within this account.
# Does not appear in any API responses.
# Ref: https://apisb.etrade.com/docs/api/order/api-order-v1.html#/definitions/OrderDetail
def preview_shares_order(etrade_oauth: ETradeOAuth, etrade_order: ETradeOrder, account_id_key: str, symbol: str,
                         limit_price: float, quantity: int, print_payload: bool, print_response: bool, 
                         order_type: str = 'EQ', order_action: str = 'BUY', all_or_none: bool = False,
                         client_order_id: str = str(randint(1000000000, 9999999999)), stop_price: float = 0,
                         price_type: str = 'LIMIT', market_session: str = 'REGULAR', 
                         order_term: str = 'IMMEDIATE_OR_CANCEL', routing_destination: str = 'AUTO') -> tuple[dict, int]:

    preview_order_params = {
        'PreviewOrderRequest': {
            'orderType': order_type,
            'clientOrderId': client_order_id,
            'Order': {
                'allOrNone': all_or_none,
                'priceType': price_type,
                'orderTerm': order_term,
                'marketSession': market_session,
                'stopPrice': stop_price,
                'limitPrice': limit_price,
                'routingDestination': routing_destination,
                'Instrument': {
                    'Product': {
                        'securityType': 'EQ',
                        'symbol': symbol
                    },
                    'orderAction': order_action,
                    'quantityType': 'QUANTITY',
                    'quantity': quantity,
                }
            }
        }
    }

    preview_order_data = _post_etrade_req(etrade_oauth, etrade_order, account_id_key, preview_order_params,
                                          'preview', print_payload, print_response)

    return preview_order_params['PreviewOrderRequest'], preview_order_data['PreviewOrderResponse']['PreviewIds']['previewId']

My place_shares_order function, this is similar to the above function. This takes the preview_order_params and the preview_id from the preview_shares_order function to make the equity order. It will check and display if the order is successful or not:

def place_shares_order(etrade_oauth: ETradeOAuth, etrade_order: ETradeOrder, account_id_key: str,
                       preview_order_params: dict, preview_id: int, print_payload: bool, print_response: bool) -> None:

    # Insert preview_order_params and preview_id into new place_shares_order payload
    place_order_params = {'PlaceOrderRequest': preview_order_params}
    place_order_params['PlaceOrderRequest']['PreviewIds'] = {'previewId': preview_id}

    place_order_data = _post_etrade_order_req(etrade_oauth, etrade_order, account_id_key, place_order_params, 'place',
                                              print_payload, print_response)['PlaceOrderResponse']['Order']['messages']['Message']

    for index, msg_codes in enumerate(place_order_data):
        order_msg_code = msg_codes['code']
        order_msg_desc = msg_codes['description'].replace('|', ' - ')

        # Order success code = 1026
        # Ref: https://apisb.etrade.com/docs/api/order/api-order-v1.html#/definitions/ErrorCodes
        if order_msg_code != '1026' and index + 1 == len(place_order_data):
            raise ValueError(f"Order Msg Code: {order_msg_code}\n   Description: {order_msg_desc}")
        print(f"Order Msg Code: {order_msg_code}\n   Description: {order_msg_desc}\n")

And calling them:

preview_order_params, preview_id = preview_shares_order(etrade_oauth, etrade_order, account_id_key, 'CVNA',
                                                        1.00, 1, print_payload=False, print_response=False)

place_shares_order(etrade_oauth, etrade_order, account_id_key, preview_order_params,
                   preview_id, print_payload=False, print_response=False)

If successful your output should be:

Order Msg Code: 1026
Description: 200 - Your order was successfully entered during market hours.

You can verify even further by printing out your order history:

print('Orders:', etrade_order.list_orders(account_id_key, resp_format='json'))

@sfcheng
Copy link

sfcheng commented Jan 27, 2023

After one day of digging, I found the root cause of this stupid issue. The string key "previewIds" need be capitalized as "PreviewIds". Otherwise, the server won't recognize the previewId parameter.

@windrider09
Copy link

Looots of inconsistency in Etrade API. Sometimes the first character is capped and sometimes it isn't.

@sfcheng
Copy link

sfcheng commented Feb 9, 2023

I do have another issue with etrade api. During authentication, from time to time, I got this error. All I can do is to restart the code and then it could pass without error.

Exception has occurred: UnicodeDecodeError
'ascii' codec can't decode byte 0xe2 in position 60: ordinal not in range(128)
File "I:\projects\EtradePythonClient\auto_trade.py", line 577, in
session = etrade.get_auth_session(request_token,
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 60: ordinal not in range(128)

@m4rkyt
Copy link

m4rkyt commented Feb 16, 2023

For anyone else who comes across this thread, i was unable to get the preview_equity_order and place_equity_order functions to work but i was able to come up with this workaround.

You basically need to make the request yourself using the underlying api endpoints instead of the functions. For these function to work, you need to have already setup pyetrades oauth obj (in my case its etrade_oauth)

import json
import xmltodict
import xml.etree.ElementTree as ET

from dicttoxml2 import dicttoxml

from pyetrade.order import ETradeOrder
from pyetrade import ETradeAccounts, ETradeOAuth

Function for making the post requests to the etrade api endpoints

def _post_etrade_req(etrade_oauth: ETradeOAuth, etrade_order: ETradeOrder, account_id_key: str,
                     order_params: dict, post_endpoint: str, print_payload: bool, print_response: bool) -> dict:

    order_payload = dicttoxml(order_params, root=False, attr_type=False).decode('UTF-8')

    if print_payload:
        pretty_xml = ET.XML(order_payload)
        ET.indent(pretty_xml)
        print(f'\n{post_endpoint.capitalize()} Order Request XML Payload:\n{"="*(27+len(post_endpoint))}\n'
              f'{ET.tostring(pretty_xml, encoding="unicode")}')

    headers = {"consumerKey": etrade_oauth.consumer_key, 'Content-Type': 'application/xml'}
    post_endpoint_url = f'https://api.etrade.com/v1/accounts/{account_id_key}/orders/{post_endpoint}'

    order_response = etrade_order.session.post(post_endpoint_url, headers=headers, data=order_payload)

    order_data = json.loads(json.dumps(xmltodict.parse(order_response.text)))
    
    if print_response:
        print(f'\n{post_endpoint.capitalize()} Order JSON Response:\n{"="*(21+len(post_endpoint))}\n'
              f'{json.dumps(order_data, indent=4)}')

    return order_data

My preview_shares_order function - this returns the preview_order_params and the preview_id from the preview_order_response:

# order_action_values = ['BUY', 'SELL', 'BUY_TO_COVER', 'SELL_SHORT']
# price_types_values = ['MARKET', 'LIMIT', 'STOP', 'STOP_LIMIT', 'MARKET_ON_CLOSE']
# market_session_values = ['REGULAR', 'EXTENDED']
# order_term_values = ['GOOD_UNTIL_CANCEL', 'GOOD_FOR_DAY', 'IMMEDIATE_OR_CANCEL', 'FILL_OR_KILL']
# routing_destinations_values = ['AUTO', 'ARCA', 'NSDQ', 'NYSE']

# Reference number generated by developer. Used to ensure duplicate order is not submitted.
# Value can be of 20 alphanmeric characters or less Must be unique within this account.
# Does not appear in any API responses.
# Ref: https://apisb.etrade.com/docs/api/order/api-order-v1.html#/definitions/OrderDetail
def preview_shares_order(etrade_oauth: ETradeOAuth, etrade_order: ETradeOrder, account_id_key: str, symbol: str,
                         limit_price: float, quantity: int, print_payload: bool, print_response: bool, 
                         order_type: str = 'EQ', order_action: str = 'BUY', all_or_none: bool = False,
                         client_order_id: str = str(randint(1000000000, 9999999999)), stop_price: float = 0,
                         price_type: str = 'LIMIT', market_session: str = 'REGULAR', 
                         order_term: str = 'IMMEDIATE_OR_CANCEL', routing_destination: str = 'AUTO') -> tuple[dict, int]:

    preview_order_params = {
        'PreviewOrderRequest': {
            'orderType': order_type,
            'clientOrderId': client_order_id,
            'Order': {
                'allOrNone': all_or_none,
                'priceType': price_type,
                'orderTerm': order_term,
                'marketSession': market_session,
                'stopPrice': stop_price,
                'limitPrice': limit_price,
                'routingDestination': routing_destination,
                'Instrument': {
                    'Product': {
                        'securityType': 'EQ',
                        'symbol': symbol
                    },
                    'orderAction': order_action,
                    'quantityType': 'QUANTITY',
                    'quantity': quantity,
                }
            }
        }
    }

    preview_order_data = _post_etrade_req(etrade_oauth, etrade_order, account_id_key, preview_order_params,
                                          'preview', print_payload, print_response)

    return preview_order_params['PreviewOrderRequest'], preview_order_data['PreviewOrderResponse']['PreviewIds']['previewId']

My place_shares_order function, this is similar to the above function. This takes the preview_order_params and the preview_id from the preview_shares_order function to make the equity order. It will check and display if the order of successful or not:

def place_shares_order(etrade_oauth: ETradeOAuth, etrade_order: ETradeOrder, account_id_key: str,
                       preview_order_params: dict, preview_id: int, print_payload: bool,print_response: bool) -> None:

    # Insert preview_order_params and preview_id into new place_shares_order payload
    place_order_params = {'PlaceOrderRequest': preview_order_params}
    place_order_params['PlaceOrderRequest']['PreviewIds'] = {'previewId': preview_id}

    place_order_data = _post_etrade_req(etrade_oauth, etrade_order, account_id_key, place_order_params,
                                        'place', print_payload, print_response)

    order_msg_code = place_order_data['PlaceOrderResponse']['Order']['messages']['Message']['code']
    order_msg_desc = place_order_data['PlaceOrderResponse']['Order']['messages']['Message']['description'].replace('|', ' - ')

    # Order success code = 1026
    # Ref: https://apisb.etrade.com/docs/api/order/api-order-v1.html#/definitions/ErrorCodes
    if order_msg_code != '1026':
        raise ValueError(f"Order Msg Code: {order_msg_code}\n   Description: {order_msg_desc}")
    print(f"\nOrder Msg Code: {order_msg_code}\n   Description: {order_msg_desc}\n")

And calling them:

preview_order_params, preview_id = preview_shares_order(etrade_oauth, etrade_order, account_id_key, 'CVNA',
                                                        1.00, 1, print_payload=False, print_response=False)

place_shares_order(etrade_oauth, etrade_order, account_id_key, preview_order_params,
                   preview_id, print_payload=False, print_response=False)

If successful your output should be:

Order Msg Code: 1026
Description: 200 - Your order was successfully entered during market hours.

You can verify even further by printing out your order history:

print('Orders:', etrade_order.list_orders(account_id_key, resp_format='json'))

@m4rkyt
Copy link

m4rkyt commented Feb 16, 2023

Hi, I've been trying to automate some options trades using pytrade/etrade for some months and while I can get individual legs to excersise, there doesn't seem to be support for spreads using pyetrade. Hense I've been looking at your code example here and trying to modify that to suit my needs. Starting with the code as is, I don't seem able to get passed the etrade_oauth part. I see NameError: name 'etrade_oauth' is not defined. Did you mean: 'ETradeOAuth'? Would anyone be able to help me integrate the above code into Jesse's original outh example ?

@Robert-Zacchigna
Copy link
Contributor

Robert-Zacchigna commented Feb 16, 2023

Hi, I've been trying to automate some options trades using pytrade/etrade for some months and while I can get individual legs to excersise, there doesn't seem to be support for spreads using pyetrade. Hense I've been looking at your code example here and trying to modify that to suit my needs. Starting with the code as is, I don't seem able to get passed the etrade_oauth part. I see NameError: name 'etrade_oauth' is not defined. Did you mean: 'ETradeOAuth'? Would anyone be able to help me integrate the above code into Jesse's original outh example ?

My code above assumed you already have setup ETradeOAuth and EtradeOrder

For EtradeOAuth, in my case i named the variable etrade_oauth:

etrade_oauth = ETradeOAuth(consumer_key, consumer_secret)

You then need to create the ETradeOrder obj, in my case i named it etrade_order:

etrade_order = ETradeOrder(
    consumer_key, consumer_secret,
    oauth_token, oauth_secret, dev=False  # False for PROD, True for SANDBOX
)

Once the above is done, then you can run my code above, which i think still works. I've made some changes since then i think.

@jessecooper
Copy link
Owner

@Robert-Zacchigna Looks like there is a short coming in the pyetrade lib that is corrected by your function. Would you mind opening up a PR that would add your method to the ETradeOrder object? That would be very appreciated I am sure by everyone in the community.

@m4rkyt
Copy link

m4rkyt commented Feb 16, 2023

Thanks @Robert-Zacchigna that's working fine now for equity orders ! I will modify the fields to support Options orders insteadf of Equity orders and hopefully eventually get a spread to work also. Much apprecaited.

@Robert-Zacchigna
Copy link
Contributor

Robert-Zacchigna commented Feb 17, 2023

@m4rkyt No problem, glad you were able to make it work.

@jessecooper I'd be happy to, i haven't looked too deeply into the source code so it might take me a little bit to get up to speed. By method do you mean _post_etrade_req or do you mean a fix for the preview_equity_order and place_equity_order functions in order.py?

@jessecooper
Copy link
Owner

@m4rkyt No problem, glad you were able to make it work.

@jessecooper I'd be happy to, i haven't looked too deeply into the source code so it might take me a little bit to get up to speed. By method do you mean _post_etrade_req or do you mean a fix for the preview_equity_order and place_equity_order functions in order.py?

The fix for preview_equity_order and place_equity_order in order.py

@Robert-Zacchigna
Copy link
Contributor

Robert-Zacchigna commented Feb 21, 2023

@jessecooper Ok so after digging through some things, I was actually able to get the functions already in order.py to work and place equity orders. You'll notice for both of them that i dont have resp_format specified.

This works to retrieve a previewId:

client_order_id = str(randint(1000000000, 9999999999))

preview_id = etrade_order.preview_equity_order(accountId=account_id_key, symbol='SPY', clientOrderId=client_order_id, limitPrice=300.0,
                                               quantity=1, orderType='EQ', orderAction='BUY', allOrNone=False, stopPrice=0,
                                               priceType='LIMIT', marketSession='REGULAR', orderTerm='FILL_OR_KILL',
                                               routingDestination='AUTO')['PreviewOrderResponse']['PreviewIds']['previewId']
print('PreviewID:', preview_id)

This code also successfully places the equity order without a previewId (verified in etrade under the orders tab):

client_order_id = str(randint(1000000000, 9999999999))

kwargs = {'accountId': account_id_key, 'symbol': 'SPY', 'orderAction': 'BUY', 'clientOrderId': client_order_id,
          'priceType': 'LIMIT', 'quantity': 1, 'orderTerm': 'FILL_OR_KILL', 'marketSession': 'REGULAR', 'limitPrice': 300,
          'orderType': 'EQ', 'allOrNone': False, 'stopPrice': 0, 'routingDestination': 'AUTO'}

print(etrade_order.place_equity_order(**kwargs))

AND

client_order_id = str(randint(1000000000, 9999999999))

print(etrade_order.place_equity_order(accountId=account_id_key, symbol='SPY', orderAction='BUY',
                                      clientOrderId=client_order_id, priceType='LIMIT', quantity=1,
                                      orderTerm='FILL_OR_KILL', marketSession='REGULAR', limitPrice=300,
                                      orderType='EQ', allOrNone=False, stopPrice=0, routingDestination='AUTO'))

Looking at it all, i think whats going on is a combination of misunderstanding the variable accountId name, the json resp_format option and what the kwargs payload should look like.

  • First, the accountId param, this is kinda confusing because the etrade accountId is actually the 8 digit number in etrade, where these requests require the accountId key to make the request (retrieved from the etrade_accounts.list_accounts() function). Therefore, my recommendation would be to change the name of the param to accountIdKey instead.
  • Second, for whatever reason specifying json for the resp_format for either of these function always results in a http 500 error (no matter what i tried to get it to work). Since it appears that the etrade API randomly supports json and xml in various areas (honestly what the heck were these devs doing then they created the API), my recommendation would be to hard code xml as the format for these functions to avoid this issue.
  • Lastly, I think the final part of this is that people might be creating the kwargs payload incorrectly, thus throwing the 500 error once more due to the incorrect params. My recommendation would be to either provide an example in the documentation of what a basic payload should look like OR, instead of utilizing kwargs, specify the params directly in the function to avoid the confusion and allow function hinting on what is needed and what is optional for the request (for both preview and place).

I can make the PR if you agree with the above, but please double check yourself that what i stated above is the same for you before we make any changes. Let me know what you find and if you agree with my assessment, thanks.

@jessecooper
Copy link
Owner

I do agree. The orders.py module is actually fairly older code and is not following the same patterns as refactored modules like the accounts.py module.
The newer modules all use xmltodict (https://github.com/jessecooper/pyetrade/blob/master/pyetrade/accounts.py#L10) and all methods are defaulted to xml (https://github.com/jessecooper/pyetrade/blob/master/pyetrade/accounts.py#L61).
Like you have noticed the json support is spotty for whatever reason in the ETrade API. I have been meaning to refactor all modules to follow this new pattern and use type hints where possible. I agree with all your above assertions and would really appreciated the contribution and modernization of that orders module. Thanks in advance.

@Robert-Zacchigna
Copy link
Contributor

@jessecooper Sounds good, I've given it all a first run through, please check it out here and let me know what you think: #78

@jessecooper
Copy link
Owner

@Robert-Zacchigna Thank you for doing this. Give me a bit of time to look at the changes.

@mw66
Copy link
Contributor

mw66 commented Apr 13, 2023

... hopefully eventually get a spread to work also.

@m4rkyt are you able to get the spread to work? can you create a PR to share the code?

@m4rkyt
Copy link

m4rkyt commented Apr 14, 2023

Hi @mw66,
Currently my code still places all option legs individually and waits for confirmation on long fills before placing the short since my account won't let me trade naked shorts. It's a bit messy doing this as I can lose a few seconds waiting for confirmation of a fill so I do plan to modify this to use a spread. I will try this over the next couple of weeks and report back.

@windrider09
Copy link

windrider09 commented Apr 14, 2023

... hopefully eventually get a spread to work also.

@m4rkyt are you able to get the spread to work? can you create a PR to share the code?

I'd love to see the spread order working too.

@korivernon
Copy link

Hi all, I'm new here. I'm trying to call the preview_equity_order method along with the place_equity_order method. I have a pretty lengthy question with two parts (1 is for options order, 2 is for equity order). I'm sure I'm doing something wrong somewhere and I'd appreciate some assistance with order functionality!

  1. I attempted to place an option order but I get the following response:
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='api.etrade.com', port=443): Read timed out.

Below is what I pass in to preview_equity_order

etrade_order.preview_equity_order(
        securityType="OPTN",
        orderType="OPTN",
        accountIdKey="XXXXXXXXXXXXXX",
        symbol="BAC",
        orderAction="SELL_CLOSE",
        clientOrderId="6591041807",
        priceType="MARKET",
        quantity=3,
        callPut="CALL",
        expiryDate=datetime.datetime(2023,4,21), # modified to take datetime object
        marketSession="REGULAR",
        orderTerm= "GOOD_FOR_DAY",
        strikePrice=30
    )

Here is the Payload

{ 
'PreviewOrderRequest': {
      'orderType': 'OPTN', 
      'clientOrderId': '6591041807', 
      'Order': {
            'securityType': 'OPTN', 
            'orderType': 'OPTN', 
            'accountIdKey': 'zBRmF6b_gzE-mkO74Vbaag', 
            'symbol': 'BAC', 
            'orderAction':  'SELL_CLOSE', 
            'clientOrderId': '6591041807', 
            'priceType': 'MARKET', 
            'quantity': 3, 
            'callPut': 'CALL', 
            'marketSession': 'REGULAR', 
            'orderTerm': 'GOOD_FOR_DAY', 
            'strikePrice': 30, 'Instrument': {
                        'Product': {
                              'securityType': 'OPTN', 
                              'symbol': 'BAC', 
                              'expiryDay': 21, 
                              'expiryMonth': 4, 
                              'expiryYear': 2023, 
                              'callPut': 'CALL', 
                              'strikePrice': 30
                                    }, 
                        'orderAction': 'SELL_CLOSE', 
                        'quantityType': 'QUANTITY', 
                        'quantity': 3
                  }
            }
      }
}
  1. Also having an issue when trying to place an equity order.
etrade_order.preview_equity_order(
        securityType="EQ",
        orderType="EQ",
        accountIdKey="XXXXXXXXXXXXXX",
        symbol="AAPL",
        orderAction="SELL",
        clientOrderId="6591041907",
        priceType="MARKET",
        quantity=1,
        marketSession="REGULAR",
        orderTerm="GOOD_FOR_DAY",
    )

Below is the preview order request.

{
'PreviewOrderRequest':  {
      'orderType': 'EQ', 
      'clientOrderId': '6591041907', 
      'Order': {
            'securityType': 'EQ', 
            'orderType': 'EQ', 
            'accountIdKey': 'XXXXXXXXXXXXXXX, 
            'symbol': 'AAPL', 
            'orderAction': 'SELL', 
            'clientOrderId': '6591041907', 
            'priceType': 'MARKET', 
            'quantity': 1, 
            'marketSession': 'REGULAR', 
            'orderTerm': 'GOOD_FOR_DAY', 
            'Instrument': {
                  'Product': {
                        'securityType': 'EQ', 
                        'symbol': 'AAPL'
                  }, 
            'orderAction': 'SELL', 
            'quantityType': 'QUANTITY', 
            'quantity': 1
                  }
            }
      }
}

I get the following response:

requests.exceptions.ConnectionError: HTTPSConnectionPool(host='api.etrade.com', port=443): Read timed out.

Thanks for taking a look!
-Kori

@Robert-Zacchigna
Copy link
Contributor

@korivernon I havent seen that error before, could be something with your network or something else.

  • Are you able to make other calls with the api?
  • Did you try this during trading hours or after hours?
  • Furthermore, one of the weird quirks of the API is that it wont let you preview or attempt to place an equity order if the order itself is more than the available cash you have to spend in your account. Don't know why it is that way, just something I've observed during my usage with the API.
    • Although that would usually kick out a different error than the one above (which is why i think it could be something with your network connection).

@korivernon
Copy link

korivernon commented Apr 16, 2023 via email

@korivernon
Copy link

korivernon commented Apr 17, 2023

I'm still getting an issue trying to submit my order (using my own order version) during market hours... Successfully Previews but I get this

Error: For your protection, we have timed out your original order request. If you would like to place this order, please resubmit it now.

When I use the order functionality from EtradeOrder, I get this issue:

xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 4

Does anyone mind sending their kwargs with sensitive information redacted? Below are mine for an options order. I modified to take in datetime object

{'securityType': 'OPTN', 
'orderType': 'OPTN', 
'accountIdKey': 'XXXXXXXXXXX', 
'symbol': 'BAC', 
'orderAction': 'SELL_CLOSE', 
'clientOrderId': '6591041807', 
'priceType': 'MARKET', 
'quantity': 3, 
'callPut': 'CALL', 
'expiryDate': datetime.datetime(2023, 4, 21, 0, 0), 
'marketSession': 'REGULAR', 
'orderTerm': 'GOOD_FOR_DAY', 
'strikePrice': 30}

Thanks,
Kori

@Robert-Zacchigna
Copy link
Contributor

@korivernon I think you might be missing some params, try adding these 3 params as well to your kwargs:

"allOrNone": False, "stopPrice": 0, "routingDestination": 'AUTO'

The etrade api uses a mix of json and xml endpoints (who knows why), so the error above seems to indicate the xml payload is malformed (which could be the result of missing params).

I dont have much experience with options orders but it should be similar enough to equity orders.

@korivernon
Copy link

korivernon commented Apr 18, 2023

@korivernon I think you might be missing some params, try adding these 3 params as well to your kwargs:

"allOrNone": False, "stopPrice": 0, "routingDestination": 'AUTO'

The etrade api uses a mix of json and xml endpoints (who knows why), so the error above seems to indicate the xml payload is malformed (which could be the result of missing params).

I dont have much experience with options orders but it should be similar enough to equity orders.

It seems like that may have done the trick!

Sometimes the read times out. Other times it seems to execute without issue. This may be because I'm trying to place the order outside of market hours, so I'll give it another try in the morning.

In the meantime, have you ever gotten this:

requests.exceptions.ConnectionError: HTTPSConnectionPool(host='api.etrade.com', port=443): Read timed out.

Additionally, when it "seems to run without issue," this is the dictionary that is the result of the preview_equity_order function. This is not because I'm outside of market hours, as I would expect a 200 response ( 200 | The market was closed when we received your order. It has been entered into our system and will be reviewed prior to market open on the next regular trading day. After market open, please check to make sure your order was accepted.).

{'html': {'body': {'b': 'Http/1.1 400 Bad Request'}}}

Here are the kwargs I'm passing in:

{
'securityType': 'OPTN', 
'orderType': 'OPTN', 
'accountIdKey': 'XXXXXXXXXX', 
'symbol': 'BAC', 
'orderAction': 'SELL_CLOSE', 
'clientOrderId': '6591041807', 
'priceType': 'MARKET', 
'quantity': 3, 
'callPut': 'CALL', 
'expiryDate': datetime.datetime(2023, 4, 21, 0, 0), 
'marketSession': 'REGULAR', 
'orderTerm': 'GOOD_FOR_DAY', 
'strikePrice': 30, 
'allOrNone': False, 
'stopPrice': 0, 
'routingDestination': 'AUTO'
}

@Robert-Zacchigna
Copy link
Contributor

I see you mentioned using a custom order function, did you by chance specify a timeout in the request? From what i can tell its possible the timeout is being reached before the request has time to be completed.

As for the 400 error im not too sure, it strange that it happens when you call the preview_equity_order function directly and not when you try to place an equity as well.

I say that because if you look at the code for the order functions, preview_equity_order is called within the place_equity_order function and the place_options_order function calls the place_equity_order function.

  • Just to be clear: option_order -> place_order -> preview_order

If the order is executed then i wouldn't be too concerned about it, i would take a guess that it might be related to the timeout issue.

@genedragon
Copy link

I do have another issue with etrade api. During authentication, from time to time, I got this error. All I can do is to restart the code and then it could pass without error.

Exception has occurred: UnicodeDecodeError 'ascii' codec can't decode byte 0xe2 in position 60: ordinal not in range(128) File "I:\projects\EtradePythonClient\auto_trade.py", line 577, in session = etrade.get_auth_session(request_token, UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 60: ordinal not in range(128)

Has anyone stumbled across and figured out this issue?

@genedragon
Copy link

UPDATE: appears that my VPN was playing some role here. Connecting to VPN resolved the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests