PageRenderTime 323ms CodeModel.GetById 40ms RepoModel.GetById 1ms app.codeStats 0ms

/src/googlecl/finance/service.py

http://googlecl.googlecode.com/
Python | 255 lines | 236 code | 2 blank | 17 comment | 0 complexity | 8a2b4d76649162dd5f35ed461a32bef4 MD5 | raw file
  1. # Copyright (C) 2010 Google Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Service details and instances for the Finance service.
  15. Some use cases:
  16. Create portfolio:
  17. finance create --title "Some Portfolio" --currency USD
  18. Delete portfolio:
  19. finance delete --title "Some Portfolio"
  20. List portfolios:
  21. finance list
  22. Create position:
  23. finance create-pos --title "Some Portfolio" --ticker NYSE:PCLN
  24. Delete position:
  25. finance delete-pos --title "Some Portfolio" --ticker NYSE:PCLN
  26. List positions:
  27. finance list-pos --title "Some Portfolio"
  28. Create transaction:
  29. finance create-txn --title "Some Portfolio" --ticker NASDAQ:PCLN
  30. --currency USD --ttype Sell --price 346.60 --commission 7.7
  31. --shares 60 --date 2010-09-24 --notes "Stop loss on 347.01"
  32. Delete transaction:
  33. finance delete-txn --title "Some Portfolio"
  34. --ticker NASDAQ:PCLN --txnid 4
  35. List transactions:
  36. finance list-txn --title "Some Portfolio" --ticker NASDAQ:PCLN
  37. """
  38. __author__ = 'bartosh@gmail.com (Ed Bartosh)'
  39. import logging
  40. import datetime
  41. import googlecl
  42. import googlecl.calendar.date
  43. from googlecl.base import BaseCL
  44. from googlecl.service import BaseServiceCL
  45. from googlecl.finance import SECTION_HEADER
  46. from gdata.service import RequestError
  47. from gdata.finance.service import FinanceService, PortfolioQuery, PositionQuery
  48. from gdata.finance import PortfolioData, PortfolioEntry, TransactionEntry, \
  49. TransactionData, Money, Price, Commission, \
  50. PortfolioFeedFromString
  51. LOG = logging.getLogger(googlecl.finance.LOGGER_NAME)
  52. class FinanceServiceCL(FinanceService, BaseServiceCL):
  53. """Extends gdata.photos.service.FinanceService for the command line.
  54. This class adds some features focused on using Finance via an installed app
  55. with a command line interface.
  56. """
  57. def __init__(self, config):
  58. """Constructor."""
  59. FinanceService.__init__(self)
  60. BaseServiceCL.__init__(self, SECTION_HEADER, config)
  61. self.max_results = None
  62. def create_portfolio(self, title, currency):
  63. """Creates a portfolio.
  64. Args:
  65. title: Title to give the portfolio.
  66. currency: Currency associated with the portfolio (e.g. USD)
  67. """
  68. pfl = PortfolioEntry(
  69. portfolio_data=PortfolioData(currency_code=currency))
  70. pfl.portfolio_title = title
  71. try:
  72. return self.AddPortfolio(pfl)
  73. except RequestError, err:
  74. LOG.error('Failed to create portfolio: %s' % err[0]['body'])
  75. CreatePortfolio = create_portfolio
  76. def is_token_valid(self, test_uri='/data/feed/api/user/default'):
  77. """Check that the token being used is valid."""
  78. return BaseCL.IsTokenValid(self, test_uri)
  79. IsTokenValid = is_token_valid
  80. def get_portfolio_entries(self, title=None, returns=False, positions=False,
  81. multiple=True):
  82. """Get portfolio entries or one entry.
  83. Args:
  84. title: string, portfolio title, could be regexp.
  85. returns: [optional] boolean, include returns into the result.
  86. positions: [optional] boolean, include positions into the result.
  87. multiple: boolean, return multiple entries if True
  88. Returns: list of portfolio entries
  89. """
  90. query = PortfolioQuery()
  91. query.returns = returns
  92. query.positions = positions
  93. uri = "/finance/feeds/default/portfolios/" + query.ToUri()
  94. if multiple:
  95. return self.GetEntries(uri, titles=title,
  96. converter=PortfolioFeedFromString)
  97. else:
  98. entry = self.GetSingleEntry(uri, title=title,
  99. converter=PortfolioFeedFromString)
  100. if entry:
  101. return [entry]
  102. else:
  103. return []
  104. def get_portfolio(self, title, returns=False, positions=False):
  105. """Get portfolio by title.
  106. Args:
  107. title: string, portfolio title.
  108. returns: [optional] boolean, include returns into the result.
  109. positions: [optional] boolean, include positions into the result.
  110. Returns: portfolio feed object or None if not found.
  111. """
  112. entries = self.get_portfolio_entries(title=title, returns=returns,
  113. positions=positions, multiple=False)
  114. if entries:
  115. return entries[0]
  116. else:
  117. LOG.info('Portfolio "%s" not found' % title)
  118. return None
  119. def get_positions(self, portfolio_title, ticker_id=None,
  120. include_returns=False):
  121. """Get positions in a portfolio.
  122. Args:
  123. portfolio_title: Title of the portfolio.
  124. ticker_id: Ticker, e.g. "NYSE:GLD"
  125. include_returns: Include returns in the portfolio data. Default False.
  126. Returns:
  127. List of positions in the portfolio, or empty list if no positions found
  128. matching the criteria.
  129. """
  130. # XXX:Would be nice to differentiate between positions. Right now, just get
  131. # all of them.
  132. pfl = self.get_portfolio(portfolio_title, returns=include_returns,
  133. positions=True)
  134. if not pfl:
  135. LOG.debug('No portfolio to get positions from!')
  136. return []
  137. if not pfl.positions:
  138. LOG.debug('No positions found in this portfolio.')
  139. return []
  140. if ticker_id is not None:
  141. positions = [self.GetPosition(portfolio_id=pfl.portfolio_id,
  142. ticker_id=ticker_id)]
  143. else:
  144. positions = self.GetPositionFeed(portfolio_entry=pfl).entry
  145. return positions
  146. def get_transactions(self, portfolio_title, ticker_id, transaction_id=None):
  147. pfl = self.get_portfolio(portfolio_title)
  148. if not pfl:
  149. LOG.debug('No portfolio to get transactions from!')
  150. return []
  151. if transaction_id:
  152. transactions = [self.GetTransaction(portfolio_id=pfl.portfolio_id,
  153. ticker_id=ticker_id,
  154. transaction_id=transaction_id)]
  155. else:
  156. transactions = self.GetTransactionFeed(portfolio_id=pfl.portfolio_id,
  157. ticker_id=ticker_id).entry
  158. return transactions
  159. def create_transaction(self, pfl, ttype, ticker, shares=None, price=None,
  160. currency=None, commission=None, date='', notes=None):
  161. """Create transaction.
  162. Args:
  163. pfl: portfolio object.
  164. ttype: string, transaction type, on of the 'Buy', 'Sell',
  165. 'Short Sell', 'Buy to Cover'.
  166. shares: [optional] decimal, amount of shares.
  167. price: [optional] decimal, price of the share.
  168. currency: [optional] string, portfolio currency by default.
  169. commission: [optional] decimal, brocker commission.
  170. date: [optional] string, transaction date,
  171. datetime.now() by default.
  172. notes: [optional] string, notes.
  173. Returns:
  174. None if transaction created successfully, otherwise error string.
  175. """
  176. if not currency:
  177. currency = pfl.portfolio_data.currency_code
  178. if date is None:
  179. # if date is not provided from the command line current date is set
  180. date = datetime.datetime.now().isoformat()
  181. elif date is '':
  182. # special case for create position task. date should be set to None
  183. # to create empty transaction. See detailed explanations in
  184. # the _run_create_position function below
  185. date = None
  186. else:
  187. parser = googlecl.calendar.date.DateParser()
  188. date = parser.parse(date).local.isoformat()
  189. if price is not None:
  190. price = Price(money=[Money(amount=price, currency_code=currency)])
  191. if commission is not None:
  192. commission = Commission(money=[Money(amount=commission,
  193. currency_code=currency)])
  194. txn = TransactionEntry(transaction_data=TransactionData(
  195. type=ttype, price=price, shares=shares, commission=commission,
  196. date=date, notes=notes))
  197. try:
  198. return self.AddTransaction(txn, portfolio_id=pfl.portfolio_id,
  199. ticker_id=ticker)
  200. except RequestError, err:
  201. LOG.error('Failed to create transaction: %s' % err[0]['body'])
  202. SERVICE_CLASS = FinanceServiceCL
  203. # Local Variables:
  204. # mode: python
  205. # py-indent-offset: 2
  206. # indent-tabs-mode: nil
  207. # tab-width: 2
  208. # End: