def assign_to_portfolio(self, orderID=0, loanIDs=[], returnJson=False): """ Assign an order to a the portfolio named in the investing dictionary. If returnJson is True, this method will return the JSON returned from the server (this is primarily for unit testing) Otherwise it returns the name of the portfolio the order was assigned to or False """ # Assign to portfolio resText = '' try: if not self.settings['portfolio']: return True if len(loanIDs) != 0 and orderID != 0: # Data orderIDs = [orderID]*len(loanIDs) # 1 order ID per record postData = { 'loan_id': loanIDs, 'record_id': loanIDs, 'order_id': orderIDs } paramData = { 'method': 'addToLCPortfolio', 'lcportfolio_name': self.settings['portfolio'] } # New portfolio folioList = self.get_portfolio_list() if self.settings['portfolio'] not in folioList: paramData['method'] = 'createLCPortfolio' # Send response = util.post_url('/data/portfolioManagement', params=paramData, data=postData) resText = response.text resJson = response.json() if returnJson is True: return resJson # Failed if the response is not 200 or JSON result is not success if response.status_code != 200 or resJson['result'] != 'success': self.logger.error('Could not assign order #{0} to portfolio \'{1}: Server responded with {2}\''.format(str(orderID), self.settings['portfolio'], response.text)) # Success else: # Assigned to another portfolio, for some reason, raise warning if 'portfolioName' in resJson and resJson['portfolioName'] != self.settings['portfolio']: self.logger.warning('Added order #{0} to portfolio "{1}" - NOT - "{2}", and I don\'t know why'.format(str(orderID), resJson['portfolioName'], self.settings['portfolio'])) # Assigned to the correct portfolio else: self.logger.info('Added order #{0} to portfolio "{1}"'.format(str(orderID), self.settings['portfolio'])) return resJson['portfolioName'] except Exception as e: self.logger.error('Could not assign order #{0} to portfolio \'{1}\': {2} -- {3}'.format(orderID, self.settings['portfolio'], str(e), resText)) return False
def browse_notes(self): """ Sends the filters to the Browse Notes API and returns a JSON of the notes found """ try: # Get all investment options filters = util.get_filter_json(self.settings['filters']) print 'Filter!\n', filters if filters is False: filters = 'default' payload = { 'method': 'search', 'filter': filters #'filter': '[{"m_id":39,"m_metadata":{"m_controlValues":[{"value":"Year3","label":"36-month","sqlValue":null,"valueIndex":0},{"value":"Year5","label":"60-month","sqlValue":null,"valueIndex":1}],"m_type":"MVAL","m_rep":"CHKBOX","m_label":"Term (36 - 60 month)","id":39,"m_onHoverHelp":"Select the loan maturities you are interested to invest in","m_className":"classname","m_defaultValue":[{"value":"Year3","label":"36-month","sqlValue":null,"valueIndex":0},{"value":"Year5","label":"60-month","sqlValue":null,"valueIndex":1}]},"m_value":[{"value":"Year3","label":"36-month","sqlValue":null,"valueIndex":0},{"value":"Year5","label":"60-month","sqlValue":null,"valueIndex":1}],"m_visible":false,"m_position":0},{"m_id":38,"m_metadata":{"m_controlValues":[{"value":true,"label":"Exclude loans invested in","sqlValue":null,"valueIndex":0}],"m_type":"SVAL","m_rep":"CHKBOX","m_label":"Exclude Loans already invested in","id":38,"m_onHoverHelp":"Use this filter to exclude loans from a borrower that you have already invested in.","m_className":"classname","m_defaultValue":[{"value":true,"label":"Exclude loans invested in","sqlValue":null,"valueIndex":0}]},"m_value":[{"value":true,"label":"Exclude loans invested in","sqlValue":null,"valueIndex":0}],"m_visible":false,"m_position":0},{"m_id":10,"m_metadata":{"m_controlValues":[{"value":"All","label":"All","sqlValue":null,"valueIndex":0},{"value":"D","label":"<span class=\\"grades d-loan-grade\\">D</span> 18.76%","sqlValue":null,"valueIndex":1},{"value":"A","label":"<span class=\\"grades a-loan-grade\\">A</span> 7.41%","sqlValue":null,"valueIndex":2},{"value":"E","label":"<span class=\\"grades e-loan-grade\\">E</span> 21.49%","sqlValue":null,"valueIndex":3},{"value":"B","label":"<span class=\\"grades b-loan-grade\\">B</span> 12.12%","sqlValue":null,"valueIndex":4},{"value":"F","label":"<span class=\\"grades f-loan-grade\\">F</span> 23.49%","sqlValue":null,"valueIndex":5},{"value":"C","label":"<span class=\\"grades c-loan-grade\\">C</span> 15.80%","sqlValue":null,"valueIndex":6},{"value":"G","label":"<span class=\\"grades g-loan-grade\\">G</span> 24.84%","sqlValue":null,"valueIndex":7}],"m_type":"MVAL","m_rep":"CHKBOX","m_label":"Interest Rate","id":10,"m_onHoverHelp":"Specify the interest rate ranges of the notes you are willing to invest in.","m_className":"short","m_defaultValue":[{"value":"All","label":"All","sqlValue":null,"valueIndex":0}]},"m_value":[{"value":"A","label":"<span class=\\"grades a-loan-grade\\">A</span> 7.41%","sqlValue":null,"valueIndex":2},{"value":"B","label":"<span class=\\"grades b-loan-grade\\">B</span> 12.12%","sqlValue":null,"valueIndex":4},{"value":"C","label":"<span class=\\"grades c-loan-grade\\">C</span> 15.80%","sqlValue":null,"valueIndex":6}],"m_visible":false,"m_position":0},{"m_id":37,"m_metadata":{"m_controlValues":null,"m_type":"SVAL","m_rep":"TEXTBOX","m_label":"Keyword","id":37,"m_onHoverHelp":"Type any keyword","m_className":"classname","m_defaultValue":[]},"m_value":null,"m_visible":false,"m_position":0}]' } response = util.post_url('/browse/browseNotesAj.action', data=payload) jsonRes = response.json() return jsonRes except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() traceback.print_tb(exc_traceback, limit=10, file=sys.stdout) self.logger.error(str(e)) return False
def get_investment_option(self, cash): """ When investing, lending club provides a list of investment portfolio options, all with different diversification of loan classes which come out to an average percent return. This method returns an investment option that best matches your available cash and min/max percentage (defined in investing) desired. If there are multiple options between min & max, the one closest to max will be chosen. """ try: maxPercent = self.settings['maxPercent'] minPercent = self.settings['minPercent'] # Get all investment options filters = util.get_filter_json(self.settings['filters']) if filters is False: filters = 'default' payload = { 'amount': cash, 'max_per_note': 0, 'filter': filters } response = util.post_url('/portfolio/lendingMatchOptionsV2.action', data=payload) json = response.json() if json['result'] == 'success' and 'lmOptions' in json: options = json['lmOptions'] lastOption = False # Loop through all the investment options i = 0 for option in options: option['optIndex'] = i # A perfect match if option['percentage'] == maxPercent: return option # Over the max elif option['percentage'] > maxPercent: break # Over the minimum elif option['percentage'] >= minPercent: lastOption = option i += 1 # If the perfect match wasn't found, return the last # option that was under the maximum percent return lastOption else: self.logger.error('Could not get investment portfolio options! Server responded with: {0}'.format(response.text)) return False except Exception as e: self.logger.error(str(e)) return False
def place_order(self, strutToken, cash, investmentOption): """ Place the order and get the order number, loan ID from the resulting HTML -- then assign to a portfolio The cash parameter is the amount of money invest in this order The investmentOption parameter is the investment portfolio returned by get_investment_option() """ orderID = 0 loanIDs = [] # Process order confirmation page try: payload = {} if strutToken: payload['struts.token.name'] = 'struts.token' payload['struts.token'] = strutToken response = util.post_url('/portfolio/orderConfirmed.action', data=payload) # Process HTML html = response.text soup = BeautifulSoup(html) # Order num orderField = soup.find(id='order_id') if orderField: orderID = int(orderField['value']) # Load ID loanTags = soup.find_all('td', {'class': 'loan_id'}) for tag in loanTags: loanIDs.append(int(tag.text)) # Print status message if orderID == 0: self.logger.error('An investment order was submitted, but a confirmation could not be determined') else: self.logger.info('Order #{0} was successfully submitted for ${1} at {2}%'.format(orderID, cash, investmentOption['percentage'])) # Print order summary orderSummary = self.get_option_summary(investmentOption) self.logger.info(orderSummary) except Exception as e: self.logger.error('Could not get your order number or loan ID from the order confirmation. Err Message: {0}'.format(str(e))) return (orderID, loanIDs)
def authenticate(self): """ Attempt to authenticate the user with the email/pass in the investing dictionary. Returns True/False """ payload = { 'login_email': self.settings.auth['email'], 'login_password': self.settings.auth['pass'] } response = util.post_url('/account/login.action', data=payload, useCookies=False) if (response.status_code == 200 or response.status_code == 302) and 'LC_FIRSTNAME' in response.cookies: self.authed = True util.cookies = response.cookies return True self.logger.error('Authentication returned {0}. Cookies: {1}'.format(response.status_code, str(response.cookies.keys()))) return False