PageRenderTime 185ms CodeModel.GetById 100ms app.highlight 17ms RepoModel.GetById 64ms 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
 15
 16"""Service details and instances for the Finance service.
 17
 18Some use cases:
 19Create portfolio:
 20  finance create --title "Some Portfolio" --currency USD
 21
 22Delete portfolio:
 23  finance delete --title "Some Portfolio"
 24
 25List portfolios:
 26  finance list
 27
 28Create position:
 29  finance create-pos --title "Some Portfolio" --ticker NYSE:PCLN
 30
 31Delete position:
 32  finance delete-pos --title "Some Portfolio" --ticker NYSE:PCLN
 33
 34List positions:
 35  finance list-pos --title "Some Portfolio"
 36
 37Create transaction:
 38  finance create-txn --title "Some Portfolio" --ticker NASDAQ:PCLN
 39          --currency USD --ttype Sell --price 346.60 --commission 7.7
 40          --shares 60 --date 2010-09-24 --notes "Stop loss on 347.01"
 41
 42Delete transaction:
 43  finance delete-txn --title "Some Portfolio"
 44                             --ticker NASDAQ:PCLN --txnid 4
 45
 46List transactions:
 47  finance list-txn --title "Some Portfolio" --ticker NASDAQ:PCLN
 48
 49"""
 50
 51__author__ = 'bartosh@gmail.com (Ed Bartosh)'
 52
 53import logging
 54import datetime
 55
 56import googlecl
 57import googlecl.calendar.date
 58from googlecl.base import BaseCL
 59from googlecl.service import BaseServiceCL
 60from googlecl.finance import SECTION_HEADER
 61
 62from gdata.service import RequestError
 63from gdata.finance.service import FinanceService, PortfolioQuery, PositionQuery
 64from gdata.finance import PortfolioData, PortfolioEntry, TransactionEntry, \
 65                          TransactionData, Money, Price, Commission, \
 66                          PortfolioFeedFromString
 67
 68LOG = logging.getLogger(googlecl.finance.LOGGER_NAME)
 69
 70class FinanceServiceCL(FinanceService, BaseServiceCL):
 71
 72  """Extends gdata.photos.service.FinanceService for the command line.
 73
 74  This class adds some features focused on using Finance via an installed app
 75  with a command line interface.
 76
 77  """
 78
 79  def __init__(self, config):
 80    """Constructor."""
 81    FinanceService.__init__(self)
 82    BaseServiceCL.__init__(self, SECTION_HEADER, config)
 83    self.max_results = None
 84
 85  def create_portfolio(self, title, currency):
 86    """Creates a portfolio.
 87
 88    Args:
 89      title: Title to give the portfolio.
 90      currency: Currency associated with the portfolio (e.g. USD)
 91    """
 92    pfl = PortfolioEntry(
 93      portfolio_data=PortfolioData(currency_code=currency))
 94    pfl.portfolio_title = title
 95    try:
 96      return self.AddPortfolio(pfl)
 97    except RequestError, err:
 98      LOG.error('Failed to create portfolio: %s' % err[0]['body'])
 99
100  CreatePortfolio = create_portfolio
101
102  def is_token_valid(self, test_uri='/data/feed/api/user/default'):
103    """Check that the token being used is valid."""
104    return BaseCL.IsTokenValid(self, test_uri)
105
106  IsTokenValid = is_token_valid
107
108  def get_portfolio_entries(self, title=None, returns=False, positions=False,
109                            multiple=True):
110    """Get portfolio entries or one entry.
111    Args:
112      title: string, portfolio title, could be regexp.
113      returns: [optional] boolean, include returns into the result.
114      positions: [optional] boolean, include positions into the result.
115      multiple: boolean, return multiple entries if True
116    Returns: list of portfolio entries
117    """
118
119    query = PortfolioQuery()
120    query.returns = returns
121    query.positions = positions
122
123    uri = "/finance/feeds/default/portfolios/" + query.ToUri()
124
125    if multiple:
126      return self.GetEntries(uri, titles=title,
127                             converter=PortfolioFeedFromString)
128    else:
129      entry = self.GetSingleEntry(uri, title=title,
130                                  converter=PortfolioFeedFromString)
131      if entry:
132        return [entry]
133      else:
134        return []
135
136  def get_portfolio(self, title, returns=False, positions=False):
137    """Get portfolio by title.
138    Args:
139      title: string, portfolio title.
140      returns: [optional] boolean, include returns into the result.
141      positions: [optional] boolean, include positions into the result.
142
143    Returns: portfolio feed object or None if not found.
144    """
145
146    entries = self.get_portfolio_entries(title=title, returns=returns,
147                                         positions=positions, multiple=False)
148    if entries:
149      return entries[0]
150    else:
151      LOG.info('Portfolio "%s" not found' % title)
152      return None
153
154  def get_positions(self, portfolio_title, ticker_id=None,
155                    include_returns=False):
156    """Get positions in a portfolio.
157
158    Args:
159      portfolio_title: Title of the portfolio.
160      ticker_id: Ticker, e.g. "NYSE:GLD"
161      include_returns: Include returns in the portfolio data. Default False.
162
163    Returns:
164      List of positions in the portfolio, or empty list if no positions found
165      matching the criteria.
166    """
167    # XXX:Would be nice to differentiate between positions.  Right now, just get
168    # all of them.
169    pfl = self.get_portfolio(portfolio_title, returns=include_returns,
170                               positions=True)
171    if not pfl:
172      LOG.debug('No portfolio to get positions from!')
173      return []
174    if not pfl.positions:
175      LOG.debug('No positions found in this portfolio.')
176      return []
177
178    if ticker_id is not None:
179      positions = [self.GetPosition(portfolio_id=pfl.portfolio_id,
180                                      ticker_id=ticker_id)]
181    else:
182      positions = self.GetPositionFeed(portfolio_entry=pfl).entry
183    return positions
184
185  def get_transactions(self, portfolio_title, ticker_id, transaction_id=None):
186    pfl = self.get_portfolio(portfolio_title)
187    if not pfl:
188      LOG.debug('No portfolio to get transactions from!')
189      return []
190    if transaction_id:
191      transactions = [self.GetTransaction(portfolio_id=pfl.portfolio_id,
192                                            ticker_id=ticker_id,
193                                            transaction_id=transaction_id)]
194    else:
195      transactions = self.GetTransactionFeed(portfolio_id=pfl.portfolio_id,
196                                             ticker_id=ticker_id).entry
197    return transactions
198
199  def create_transaction(self, pfl, ttype, ticker, shares=None, price=None,
200                         currency=None, commission=None, date='', notes=None):
201    """Create transaction.
202
203    Args:
204      pfl: portfolio object.
205      ttype: string, transaction type, on of the 'Buy', 'Sell',
206             'Short Sell', 'Buy to Cover'.
207      shares: [optional] decimal, amount of shares.
208      price: [optional] decimal, price of the share.
209      currency: [optional] string, portfolio currency by default.
210      commission: [optional] decimal, brocker commission.
211      date: [optional] string, transaction date,
212            datetime.now() by default.
213      notes: [optional] string, notes.
214
215    Returns:
216      None if transaction created successfully, otherwise error string.
217    """
218    if not currency:
219      currency = pfl.portfolio_data.currency_code
220    if date is None:
221      # if date is not provided from the command line current date is set
222      date = datetime.datetime.now().isoformat()
223    elif date is '':
224      # special case for create position task. date should be set to None
225      # to create empty transaction. See detailed explanations in
226      # the _run_create_position function below
227      date = None
228    else:
229      parser = googlecl.calendar.date.DateParser()
230      date = parser.parse(date).local.isoformat()
231
232    if price is not None:
233      price = Price(money=[Money(amount=price, currency_code=currency)])
234    if commission is not None:
235      commission = Commission(money=[Money(amount=commission,
236                                             currency_code=currency)])
237    txn = TransactionEntry(transaction_data=TransactionData(
238        type=ttype, price=price, shares=shares, commission=commission,
239        date=date, notes=notes))
240
241    try:
242      return self.AddTransaction(txn, portfolio_id=pfl.portfolio_id,
243                                 ticker_id=ticker)
244    except RequestError, err:
245      LOG.error('Failed to create transaction: %s' % err[0]['body'])
246
247
248SERVICE_CLASS = FinanceServiceCL
249
250# Local Variables:
251# mode: python
252# py-indent-offset: 2
253# indent-tabs-mode: nil
254# tab-width: 2
255# End: