Skip to content

Commit

Permalink
implemented common stock buys and sells
Browse files Browse the repository at this point in the history
  • Loading branch information
anthonymorast committed Aug 18, 2021
1 parent 668f4b8 commit e3824e7
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 11 deletions.
2 changes: 1 addition & 1 deletion AllyInvestPy.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: AllyInvestPy
Version: 1.0.5
Version: 1.0.6
Summary: A blackbox Ally Invest/TradeKing API interface for application developers.
Home-page: https://github.com/anthonymorast/AllyInvest.py
Author: Anthony Morast
Expand Down
8 changes: 7 additions & 1 deletion ally/URLs.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ def __init__(self, response_format="json"):
self.account_history = "accounts/{id}/history.{format}".format(format=self.format, id="{id}")
self.account_holdings = "accounts/{id}/holdings.{format}".format(format=self.format, id="{id}")

# orders
self.post_order = "accounts/{id}/orders.{format}".format(format="xml", id="{id}")

# market
self.clock = "market/clock.{format}".format(format=self.format)
self.quote = "market/ext/quotes.{format}".format(format=self.format)
Expand Down Expand Up @@ -138,6 +141,9 @@ def account_holdings_url(self):
POST accounts/:id/orders
POST accounts/:id/orders/preview
"""
def get_post_order(self):
return self.base_url + self.post_order


"""
Market
Expand Down Expand Up @@ -176,7 +182,7 @@ def toplists_url(self):
@param self - the object pointer
"""
return self.base_url + self.toplists

def options_search_url(self):
return self.base_url + self.options_search

Expand Down
3 changes: 3 additions & 0 deletions ally/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from .ally import AllyAPI
from .ally import TIME_IN_FORCE
from .ally import ORDER_TYPE
from .ally import SIDE
from .URLs import URLs
from .responses import *
from .requests import *
62 changes: 58 additions & 4 deletions ally/ally.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,20 @@ def __get_symbol_string(self, symbols):
symbols = ",".join(symbols)
return symbols

def __to_format(self, response):
def __get_fixml(self, ticker, amount, type, account, side, tif, price, sectype):
fixml = "<FIXML xmlns=\"http://www.fixprotocol.org/FIXML-5-0-SP2\">"
fixml += "<Order"
if type != ORDER_TYPE.MARKET and tif is not None:
fixml += " TmInForce=\"{}\"".format(tif)
if type != ORDER_TYPE.MARKET:
fixml += " Px=\"{}\"".format(price)
fixml += " Typ=\"{}\" Side=\"{}\" Acct=\"{}\">".format(type, side, account)
fixml += "<Instrmt SecTyp=\"{}\" Sym=\"{}\"/>".format(sectype, ticker)
fixml += "<OrdQty Qty=\"{}\"/></Order></FIXML>".format(amount)

return fixml

def __to_format(self, response, xml=False):
"""A private method to return the API response in the desired format
@param self - the object pointer
@param response - response from the Ally Invest API
Expand All @@ -105,7 +118,7 @@ def __to_format(self, response):
elif response.status_code == 414:
print("URI too long, please chunk ticker symbols.")
exit()
if self.format == "json":
if self.format == "json" and not xml:
return response.json()
else:
return ElementTree.fromstring(response.content)
Expand All @@ -119,14 +132,15 @@ def __get_data(self, url):
self.__create_auth()
return self.__to_format(requests.get(url, auth=self.auth))

def __submit_post(self, url, data):
def __submit_post(self, url, data, headers={}, usexml=False):
"""A private method to submit a post request to the Ally Invest server
@param self - the object pointer
@param url - API URL to access
@param data - payload for the HTTP request
"""
self.__create_auth()
return self.__to_format(requests.post(url, data=data, auth=self.auth))
res = requests.post(url, headers=headers, data=data, auth=self.auth)
return self.__to_format(res, usexml)

def get_accounts(self):
"""Returns all of the user's accounts."""
Expand Down Expand Up @@ -325,3 +339,43 @@ def create_watchlist(self, watchlist_name, symbols=""):
if not symbols == "":
payload["symbols"] = symbols
return self.__submit_post(self.url.post_watchlist_url(), payload)

def order_common_stock(self, ticker, shares, type, account_nbr, side,
time_in_force=None, price=None):
"""Creates an order for common stock (as opposed to options).
@param self - object pointer
@param ticker - ticker symbol of the security to purchase
@param shares - the number of shares to purchase
@param type - the order type: Market, Limit, Stop, or Stop Limit
- use the provided enum for these values
@param account_nbr - the account number for which the shares are to be purchased
@param side - the side of the trade: Buy or Sell
- use the provided enum for these values
@param time_in_force - not applicable for market orders: Day Order, Good til Cancelled, Market on Close
- use the provided enum for these values
@param price - the price to purchase the security (only for limit and stop limit orders)
"""
if price == None and type != ORDER_TYPE.MARKET:
raise("Price is required for non-market order types.")
payload = self.__get_fixml(ticker, shares, type, account_nbr, side, time_in_force, price, "CS")
headers = {
'TKI_OVERRIDE': 'true',
'Content-Type': 'application/xml',
}
url = self.url.get_post_order().format(id=account_nbr)
return self.__submit_post(url, payload, headers, True)

class TIME_IN_FORCE:
DAY = "0"
GTC = "1"
MARKET_ON_CLOSE = "7"

class ORDER_TYPE:
MARKET = "1"
LIMIT = "2"
STOP = "3"
STOP_LIMIT = "4"

class SIDE:
BUY = "1"
SELL = "2"
Binary file added dist/AllyInvestPy-1.0.6.tar.gz
Binary file not shown.
18 changes: 14 additions & 4 deletions examples/example.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from ally import *
from xml.etree.ElementTree import tostring

## These values are from Ally Invest API Applications page.
CONSUMER_KEY = "CONSUMER KEY"
Expand All @@ -15,9 +16,8 @@
print(ally.get_quote(["AAPL", "MSFT", "XLNX", "NXPI"]))
print(ally.news_search("AAPL"))
print(ally.news_search(["AAPL", "MSFT", "XLNX", "NXPI"]))


##NOTE: this is the preferred way to get quotes! The response classes are a little
##NOTE: this is the preferred way to get quotes! The response classes are a little
## easier to work with than the JSON.
quote_request = QuotesRequest(symbols=['SND', 'PRU', 'HMC'])
response = quote_request.execute(ally)
Expand All @@ -26,9 +26,19 @@
quote_request = QuotesRequest(symbols=ticker_list)
response = quote_request.execute(ally)
for quote in response.get_quotes():
# process quote data
# process quote data
print(quote)
pass

accounts_balances_request = AccountsBalancesRequest()
accounts_balances_response = accounts_balances_request.execute(ally)
print(accounts_balances_response.get_raw_data())

# Placing orders -- note that these must use XML as FIXML is passed o the calls
account = 00000000
# buy one share of intel at $50 for account number 00000000, print the results
print(tostring(ally.order_common_stock("INTC", 1, ORDER_TYPE.LIMIT, account,
SIDE.BUY, TIME_IN_FORCE.DAY, 50), 'utf-8', method="xml"))
# sell one share of Apple at market price for account number 00000000, print the results
print(tostring(ally.order_common_stock("AAPL", 1, ORDER_TYPE.MARKET, account, SIDE.SELL),
'utf-8', method="xml"))
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='AllyInvestPy',
version='1.0.5',
version='1.0.6',
description='A blackbox Ally Invest/TradeKing API interface for application developers.',
url='https://github.com/anthonymorast/AllyInvest.py',
author='Anthony Morast',
Expand Down

0 comments on commit e3824e7

Please sign in to comment.