/src/googlecl/finance/service.py
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: