def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret')
class TestOrder(unittest.TestCase): lc = None order = None logger = None def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') # Make sure session is enabled and clear self.lc.session.post('/session/enabled') self.lc.session.request('delete', '/session') # Use version 2 of browseNotesAj.json self.lc.session.post('/session', data={'browseNotesAj': '2'}) # Start order self.order = self.lc.start_order() def tearDown(self): pass def test_add(self): self.order.add(123, 50) self.assertEqual(len(self.order.loans), 1) self.assertEqual(self.order.loans[123], 50) def test_update(self): self.order.add(123, 50) self.assertEqual(self.order.loans[123], 50) self.order.add(123, 100) self.assertEqual(len(self.order.loans), 1) self.assertEqual(self.order.loans[123], 100) def test_remove(self): self.order.add(123, 50) self.order.add(234, 75) self.assertEqual(len(self.order.loans), 2) self.order.remove(234) self.assertEqual(len(self.order.loans), 1) self.assertEqual(self.order.loans[123], 50) self.assertFalse(234 in self.order.loans) def test_multiple_of_25(self): self.assertRaises(AssertionError, lambda: self.order.add(123, 0)) self.assertRaises(AssertionError, lambda: self.order.add(123, 26))
def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') # Make sure session is enabled and clear self.lc.session.post('/session/enabled') self.lc.session.request('delete', '/session')
def setUp(self): self.filters = Filter() self.filters['exclude_existing'] = False self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') response = self.lc.session.get('/filter_validation', query={'id': 1}) json_response = response.json() self.loan_list = json_response['loanFractions']
def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret12')
def request_loan_data(): ''' Requests list of loans that can be invested in, then makes individual call for details of the loans. Results stored in MongoDB database. Returns: loan_results: Results obtained from initial API request. list. loan_details: Individual loan details, obtained with individual API request of loan ids obtained in loan_results. list. ''' filter_search = { 'exclude_existing': False, 'funding_progress': 0, 'grades': { 'All': False, 'A': True, 'B': True, 'C': True, 'D': True, 'E': False, 'F': False, 'G': False }, 'term': { 'Year3': True, 'Year5': False } } club = LendingClub() filter_search = Filter(filter_search) club.authenticate() loan_results = club.search(filter_search, start_index=0, limit=1000) loan_results = loan_results['loans'] loan_ids = [loan['loan_id'] for loan in loan_results] loan_details = [] for loan_id in loan_ids: print "loan_id", loan_id request = club.session.get('/browse/loanDetailAj.action', query={'loan_id': loan_id}) loan_details.append(request.json()) time.sleep(1) return loan_results, loan_details
def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') # Make sure session is enabled and clear self.lc.session.post('/session/enabled') self.lc.session.request('delete', '/session') # Use version 3 of browseNotesAj.json self.lc.session.post('/session', data={'browseNotesAj': '3'}) # Start order self.order = self.lc.start_order()
def __init__(self, email = None, password = None, filter_ids = None): if filter_ids != None: self.main_filter_ids = filter_ids self.lc = LendingClub(email, password) self.lc.authenticate() self.saved_filters = self.lc.get_saved_filters() self.main_filters = \ filter(lambda x: x.id in self.main_filter_ids, self.saved_filters)
def __init__(self, verbose=False): """ Create an AutoInvestor instance - Set verbose to True if you want to see debugging logs """ self.verbose = verbose self.logger = util.create_logger(verbose) self.app_dir = util.get_app_directory() self.lc = LendingClub() # Set logger on lc if self.verbose: self.lc.set_logger(self.logger) # Create settings object self.settings = Settings(investor=self, settings_dir=self.app_dir, logger=self.logger, verbose=self.verbose) self.settings.investor = self # create a link back to this instance
def setUp(self): self.filters = Filter() self.filters['exclude_existing'] = False self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret12') response = self.lc.session.get('/filter_validation', query={'id': 1}) json_response = response.json() self.loan_list = json_response['loanFractions']
def request_loan_data(): ''' Requests list of loans that can be invested in, then makes individual call for details of the loans. Results stored in MongoDB database. Returns: loan_results: Results obtained from initial API request. list. loan_details: Individual loan details, obtained with individual API request of loan ids obtained in loan_results. list. ''' filter_search = {'exclude_existing': False, 'funding_progress': 0, 'grades': {'All': False, 'A': True, 'B': True, 'C': True, 'D': True, 'E': False, 'F': False, 'G': False}, 'term': {'Year3': True, 'Year5': False}} club = LendingClub() filter_search = Filter(filter_search) club.authenticate() loan_results = club.search(filter_search, start_index=0, limit=1000) loan_results = loan_results['loans'] loan_ids = [loan['loan_id'] for loan in loan_results] loan_details = [] for loan_id in loan_ids: print "loan_id", loan_id request = club.session.get('/browse/loanDetailAj.action', query={'loan_id': loan_id}) loan_details.append(request.json()) time.sleep(1) return loan_results, loan_details
class TestBatchOrder(unittest.TestCase): lc = None order = None logger = None def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') # Make sure session is enabled and clear self.lc.session.post('/session/enabled') self.lc.session.request('delete', '/session') # Use version 3 of browseNotesAj.json self.lc.session.post('/session', data={'browseNotesAj': '3'}) # Start order self.order = self.lc.start_order() def tearDown(self): pass def test_add_batch_dict(self): """ test_add_batch_dict Add a batch of dict loan objects """ self.order.add_batch([{ 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 }]) self.assertEqual(len(self.order.loans), 2) self.assertEqual(self.order.loans[123], 50) self.assertEqual(self.order.loans[234], 75) def test_add_batch_dict_amount(self): """ test_add_batch_dict_amount Add a batch dict with a batch_amount parameter value to override the individual values """ self.order.add_batch([{ 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 }], 100) self.assertEqual(len(self.order.loans), 2) self.assertEqual(self.order.loans[123], 100) self.assertEqual(self.order.loans[234], 100) def test_add_batch_list(self): """ test_add_batch_list Add a batch of IDs from a list, not a dict """ self.order.add_batch([123, 234], 75) self.assertEqual(len(self.order.loans), 2) self.assertEqual(self.order.loans[123], 75) self.assertEqual(self.order.loans[234], 75) def test_add_batch_list_no_amount(self): """ test_add_batch_list_no_amount Send a list of IDs to add_batch, without an amount """ self.assertRaises(AssertionError, lambda: self.order.add_batch([123, 234])) def test_add_batch_object(self): """ test_add_batch_object Pulling loans from the 'loan_fractions' value is no longer supported """ loanDict = { 'loan_fractions': [{ 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 }] } self.assertRaises(AssertionError, lambda: self.order.add_batch(loanDict)) def test_execute(self): self.order.add_batch([{ 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 }]) order_id = self.order.execute() self.assertNotEqual(order_id, 0) def test_execute_wrong_id(self): """ test_execute_wrong_id Server returns an ID that doesn't match an ID added to batch (345) """ self.order.add_batch([234, 345], 75) self.assertRaises(FilterValidationError, lambda: self.order.execute()) def test_execute_existing_portfolio(self): self.order.add_batch([{ 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 }]) portfolio = 'New Portfolio' order_id = self.order.execute(portfolio) self.assertNotEqual(order_id, 0) # Check portfolio name request = self.lc.session.get('/session') http_session = request.json() self.assertEqual(http_session['new_portfolio'], portfolio) def test_execute_new_portfolio(self): self.order.add_batch([{ 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 }]) portfolio = 'Existing Portfolio' order_id = self.order.execute(portfolio) self.assertNotEqual(order_id, 0) # Check portfolio name request = self.lc.session.get('/session') http_session = request.json() self.assertEqual(http_session['existing_portfolio'], portfolio) def test_double_execute(self): """ test_double_execute An order can only be executed once """ self.order.add_batch([{ 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 }]) order_id = self.order.execute() self.assertNotEqual(order_id, 0) self.assertRaises(AssertionError, lambda: self.order.execute())
class TestSavedFilters(unittest.TestCase): filters = None logger = None lc = None loan_list = None def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') def tearDown(self): pass def test_get_all_filters(self): filters = SavedFilter.all_filters(self.lc) self.assertEqual(len(filters), 2) self.assertEqual(filters[0].name, 'Filter 1') def test_get_saved_filters(self): saved = SavedFilter(self.lc, 1) self.assertEqual(saved.name, 'Filter 1') self.assertEqual(saved.id, 1) self.assertNotEqual(saved.search_string(), None) def test_validation_1(self): """ test_validation_1 Filter 1 against filter_validation 1 """ saved = SavedFilter(self.lc, 1) # Get loan list response = self.lc.session.get('/filter_validation', query={'id': 1}) json_response = response.json() self.loan_list = json_response['loanFractions'] # Validate, should fail on 'exclude_invested' try: saved.validate(self.loan_list) assert False, 'Test should fail on exclude_existing' except FilterValidationError as e: print e.criteria self.assertTrue(matches('exclude loans', e.criteria)) def test_validation_2(self): """ test_validation_2 Filter 2 against filter_validation 2 """ saved = SavedFilter(self.lc, 2) # Get loan list response = self.lc.session.get('/filter_validation', query={'id': 2}) json_response = response.json() self.loan_list = json_response['loanFractions'] # Validate, should fail on 'exclude_invested' try: saved.validate(self.loan_list) assert False, 'Test should fail on loan_purpose' except FilterValidationError as e: print e.criteria self.assertTrue(matches('loan purpose', e.criteria)) def test_validation_2_1(self): """ test_validation_2_1 Filter 2 against filter_validation 1 """ saved = SavedFilter(self.lc, 2) # Get loan list response = self.lc.session.get('/filter_validation', query={'id': 1}) json_response = response.json() self.loan_list = json_response['loanFractions'] # Validate, should not fail saved.validate(self.loan_list) def test_validation_2_3(self): """ test_validation_3 Filter 2 against filter_validation 3 """ saved = SavedFilter(self.lc, 2) # Get loan list response = self.lc.session.get('/filter_validation', query={'id': 3}) json_response = response.json() self.loan_list = json_response['loanFractions'] # Validate, should fail on 'exclude_invested' try: saved.validate(self.loan_list) assert False, 'Test should fail on grade' except FilterValidationError as e: print e.criteria self.assertTrue(matches('grade', e.criteria))
class TestLendingClub(unittest.TestCase): lc = None logger = None def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') # Make sure session is enabled and clear self.lc.session.post('/session/enabled') self.lc.session.request('delete', '/session') def tearDown(self): pass def test_cash_balance(self): cash = self.lc.get_cash_balance() self.assertEqual(cash, 216.02) def test_portfolios(self): portfolios = self.lc.get_portfolio_list() self.assertEquals(len(portfolios), 2) self.assertEquals(portfolios[0]['portfolioName'], 'Existing Portfolio') def test_build_portfolio(self): portfolio = self.lc.build_portfolio(200, 25, 15, 16) self.assertNotEqual(portfolio, False) self.assertEqual(portfolio['percentage'], 15.28) self.assertTrue('loan_fractions' in portfolio) self.assertEqual(len(portfolio['loan_fractions']), 15) def test_build_portfolio_session_fail(self): """ test_build_portfolio_session_fail" If the session isn't saved, fractions shouldn't be found, which should make the entire method return False """ # Disable session self.lc.session.post('/session/disabled') portfolio = self.lc.build_portfolio(200, 25, 15, 16) self.assertFalse(portfolio) def test_build_portfolio_no_match(self): """ test_build_portfolio_no_match" Enter a min/max percent that cannot match dummy returned JSON """ portfolio = self.lc.build_portfolio(200, 25, 17.6, 18.5) self.assertFalse(portfolio) def test_search(self): results = self.lc.search() self.assertTrue(results is not False) self.assertTrue('loans' in results) self.assertTrue(len(results['loans']) > 0)
class TestFilterValidation(unittest.TestCase): filters = None logger = None lc = None loan_list = None def setUp(self): self.filters = Filter() self.filters['exclude_existing'] = False self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') response = self.lc.session.get('/filter_validation', query={'id': 1}) json_response = response.json() self.loan_list = json_response['loanFractions'] def tearDown(self): pass def test_validation_defaults(self): """ test_validation_defaults Default filters should match """ self.assertTrue(self.filters.validate(self.loan_list)) def test_validation_grade_valid(self): self.filters['C'] = True self.assertTrue(self.filters.validate(self.loan_list)) def test_validation_grade_fail(self): self.filters['grades']['B'] = True self.assertRaises(FilterValidationError, lambda: self.filters.validate(self.loan_list)) def test_validation_term_36(self): """ test_validation_term_36 Should fail on the 60 month loan, loan_id: 12345 """ self.filters['term']['Year3'] = True self.filters['term']['Year5'] = False try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 12345) # Invalid Exception except Exception: self.assertTrue(False) def test_validation_term_60(self): """ test_validation_term_60 Should fail on the 36 month loan, loan_id: 23456 """ self.filters['term']['Year3'] = False self.filters['term']['Year5'] = True try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 23456) # Invalid Exception except Exception: self.assertTrue(False) def test_validation_progress_70(self): """ test_validation_progress_70 Loan 12345 is 91 percent funded Loan 23456 is 77 percent funded """ self.filters['funding_progress'] = 70 self.assertTrue(self.filters.validate(self.loan_list)) def test_validation_progress_90(self): """ test_validation_term_90 Should fail Loan 12345 is 91 percent funded Loan 23456 is 77 percent funded """ self.filters['funding_progress'] = 90 try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 23456) # Invalid Exception except Exception: self.assertTrue(False) def test_validation_progress_95(self): """ test_validation_progress_95 Should fail Loan 12345 is 91 percent funded Loan 23456 is 77 percent funded """ self.filters['funding_progress'] = 95 try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 12345) # Invalid Exception except Exception: self.assertTrue(False) def test_validation_exclude_existing(self): """ test_validation_exclude_existing Should fail on loan 23456, which the user is already invested in. """ self.filters['exclude_existing'] = True try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 23456) # Invalid Exception except Exception: self.assertTrue(False)
class TestBatchOrder(unittest.TestCase): lc = None order = None logger = None def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') # Make sure session is enabled and clear self.lc.session.post('/session/enabled') self.lc.session.request('delete', '/session') # Use version 3 of browseNotesAj.json self.lc.session.post('/session', data={'browseNotesAj': '3'}) # Start order self.order = self.lc.start_order() def tearDown(self): pass def test_add_batch_dict(self): """ test_add_batch_dict Add a batch of dict loan objects """ self.order.add_batch([ { 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 } ]) self.assertEqual(len(self.order.loans), 2) self.assertEqual(self.order.loans[123], 50) self.assertEqual(self.order.loans[234], 75) def test_add_batch_dict_amount(self): """ test_add_batch_dict_amount Add a batch dict with a batch_amount parameter value to override the individual values """ self.order.add_batch([ { 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 } ], 100) self.assertEqual(len(self.order.loans), 2) self.assertEqual(self.order.loans[123], 100) self.assertEqual(self.order.loans[234], 100) def test_add_batch_list(self): """ test_add_batch_list Add a batch of IDs from a list, not a dict """ self.order.add_batch([123, 234], 75) self.assertEqual(len(self.order.loans), 2) self.assertEqual(self.order.loans[123], 75) self.assertEqual(self.order.loans[234], 75) def test_add_batch_list_no_amount(self): """ test_add_batch_list_no_amount Send a list of IDs to add_batch, without an amount """ self.assertRaises( AssertionError, lambda: self.order.add_batch([123, 234]) ) def test_add_batch_object(self): """ test_add_batch_object Pulling loans from the 'loan_fractions' value is no longer supported """ loanDict = { 'loan_fractions': [ { 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 } ] } self.assertRaises( AssertionError, lambda: self.order.add_batch(loanDict) ) def test_execute(self): self.order.add_batch([ { 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 } ]) order_id = self.order.execute() self.assertNotEqual(order_id, 0) def test_execute_wrong_id(self): """ test_execute_wrong_id Server returns an ID that doesn't match an ID added to batch (345) """ self.order.add_batch([234, 345], 75) self.assertRaises( FilterValidationError, lambda: self.order.execute() ) def test_execute_existing_portfolio(self): self.order.add_batch([ { 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 } ]) portfolio = 'New Portfolio' order_id = self.order.execute(portfolio) self.assertNotEqual(order_id, 0) # Check portfolio name request = self.lc.session.get('/session') http_session = request.json() self.assertEqual(http_session['new_portfolio'], portfolio) def test_execute_new_portfolio(self): self.order.add_batch([ { 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 } ]) portfolio = 'Existing Portfolio' order_id = self.order.execute(portfolio) self.assertNotEqual(order_id, 0) # Check portfolio name request = self.lc.session.get('/session') http_session = request.json() self.assertEqual(http_session['existing_portfolio'], portfolio) def test_double_execute(self): """ test_double_execute An order can only be executed once """ self.order.add_batch([ { 'loan_id': 123, 'invest_amount': 50 }, { 'loan_id': 234, 'invest_amount': 75 } ]) order_id = self.order.execute() self.assertNotEqual(order_id, 0) self.assertRaises( AssertionError, lambda: self.order.execute() )
class Autobuy: lc = None #main_filter = 'low-risk2' saved_filters = None main_filters = [] main_filter_ids = [8641686, 9061019] def __init__(self, email = None, password = None, filter_ids = None): if filter_ids != None: self.main_filter_ids = filter_ids self.lc = LendingClub(email, password) self.lc.authenticate() self.saved_filters = self.lc.get_saved_filters() self.main_filters = \ filter(lambda x: x.id in self.main_filter_ids, self.saved_filters) def lc(self): self.lc def run_all(self): loan_ids = [] for f in self.main_filters: loan_ids = list(set(loan_ids) | set(self.run(f))) if len(loan_ids) > 0: order = self.lc.start_order() order.add_batch(loan_ids, 25) order.execute() print "%s loans processed" % len(loan_ids) return len(loan_ids) print "No loans processed" return False def run(self, _filter = None, just_loans = True): if _filter is None: raise Exception("No filter specified") results = self.lc.search(_filter) if results[u'totalRecords'] > 30: raise Exception("Filter probably broken, returning > 30 results") elif results[u'totalRecords'] == 0: return [] if not just_loans: order = self.lc.start_order() loans = [] for result in results[u'loans']: loan_id = result[u'loan_id'] additional_details = self.lc.loan_details(loan_id) if additional_details[u'currentJobTitle'] == u'n/a': continue rate = float(re.findall(r"\d+\.\d+|\d+", additional_details[u'rate'])[0]) term = int(additional_details[u'loanLength']) if (term == 36 and rate < 0.19) or rate < 0.22: continue loans.append(loan_id) if len(loans) > 0: if just_loans: return loans order.add_batch(loans, 25) order.execute() return True print "No loans found" if just_loans: return [] else: return True def __str__(self): print "Autobuyer"
class TestOrder(unittest.TestCase): lc = None order = None logger = None def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') # Make sure session is enabled and clear self.lc.session.post('/session/enabled') self.lc.session.request('delete', '/session') # Use version 2 of browseNotesAj.json self.lc.session.post('/session', data={'browseNotesAj': '2'}) # Start order self.order = self.lc.start_order() def tearDown(self): pass def test_add(self): self.order.add(123, 50) self.assertEqual(len(self.order.loans), 1) self.assertEqual(self.order.loans[123], 50) def test_update(self): self.order.add(123, 50) self.assertEqual(self.order.loans[123], 50) self.order.add(123, 100) self.assertEqual(len(self.order.loans), 1) self.assertEqual(self.order.loans[123], 100) def test_remove(self): self.order.add(123, 50) self.order.add(234, 75) self.assertEqual(len(self.order.loans), 2) self.order.remove(234) self.assertEqual(len(self.order.loans), 1) self.assertEqual(self.order.loans[123], 50) self.assertFalse(234 in self.order.loans) def test_multiple_of_25(self): self.assertRaises( AssertionError, lambda: self.order.add(123, 0) ) self.assertRaises( AssertionError, lambda: self.order.add(123, 26) )
class TestSavedFilters(unittest.TestCase): filters = None logger = None lc = None loan_list = None def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret12') def tearDown(self): pass def test_get_all_filters(self): filters = SavedFilter.all_filters(self.lc) self.assertEqual(len(filters), 2) self.assertEqual(filters[0].name, 'Filter 1') def test_get_saved_filters(self): saved = SavedFilter(self.lc, 1) self.assertEqual(saved.name, 'Filter 1') self.assertEqual(saved.id, 1) self.assertNotEqual(saved.search_string(), None) def test_validation_1(self): """ test_validation_1 Filter 1 against filter_validation 1 """ saved = SavedFilter(self.lc, 1) # Get loan list response = self.lc.session.get('/filter_validation', query={'id': 1}) json_response = response.json() self.loan_list = json_response['loanFractions'] # Validate, should fail on 'exclude_invested' try: saved.validate(self.loan_list) assert False, 'Test should fail on exclude_existing' except FilterValidationError as e: print(e.criteria) self.assertTrue(matches('exclude loans', e.criteria)) def test_validation_2(self): """ test_validation_2 Filter 2 against filter_validation 2 """ saved = SavedFilter(self.lc, 2) # Get loan list response = self.lc.session.get('/filter_validation', query={'id': 2}) json_response = response.json() self.loan_list = json_response['loanFractions'] # Validate, should fail on 'exclude_invested' try: saved.validate(self.loan_list) assert False, 'Test should fail on loan_purpose' except FilterValidationError as e: print(e.criteria) self.assertTrue(matches('loan purpose', e.criteria)) def test_validation_2_1(self): """ test_validation_2_1 Filter 2 against filter_validation 1 """ saved = SavedFilter(self.lc, 2) # Get loan list response = self.lc.session.get('/filter_validation', query={'id': 1}) json_response = response.json() self.loan_list = json_response['loanFractions'] # Validate, should not fail saved.validate(self.loan_list) def test_validation_2_3(self): """ test_validation_3 Filter 2 against filter_validation 3 """ saved = SavedFilter(self.lc, 2) # Get loan list response = self.lc.session.get('/filter_validation', query={'id': 3}) json_response = response.json() self.loan_list = json_response['loanFractions'] # Validate, should fail on 'exclude_invested' try: saved.validate(self.loan_list) assert False, 'Test should fail on grade' except FilterValidationError as e: print(e.criteria) self.assertTrue(matches('grade', e.criteria))
class TestFilterValidation(unittest.TestCase): filters = None logger = None lc = None loan_list = None def setUp(self): self.filters = Filter() self.filters['exclude_existing'] = False self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret12') response = self.lc.session.get('/filter_validation', query={'id': 1}) json_response = response.json() self.loan_list = json_response['loanFractions'] def tearDown(self): pass def test_validation_defaults(self): """ test_validation_defaults Default filters should match """ self.assertTrue(self.filters.validate(self.loan_list)) def test_validation_grade_valid(self): self.filters['C'] = True self.assertTrue(self.filters.validate(self.loan_list)) def test_validation_grade_fail(self): self.filters['grades']['B'] = True self.assertRaises( FilterValidationError, lambda: self.filters.validate(self.loan_list) ) def test_validation_term_36(self): """ test_validation_term_36 Should fail on the 60 month loan, loan_id: 12345 """ self.filters['term']['Year3'] = True self.filters['term']['Year5'] = False try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 12345) # Invalid Exception except Exception: self.assertTrue(False) def test_validation_term_60(self): """ test_validation_term_60 Should fail on the 36 month loan, loan_id: 23456 """ self.filters['term']['Year3'] = False self.filters['term']['Year5'] = True try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 23456) # Invalid Exception except Exception: self.assertTrue(False) def test_validation_progress_70(self): """ test_validation_progress_70 Loan 12345 is 91 percent funded Loan 23456 is 77 percent funded """ self.filters['funding_progress'] = 70 self.assertTrue(self.filters.validate(self.loan_list)) def test_validation_progress_90(self): """ test_validation_term_90 Should fail Loan 12345 is 91 percent funded Loan 23456 is 77 percent funded """ self.filters['funding_progress'] = 90 try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 23456) # Invalid Exception except Exception: self.assertTrue(False) def test_validation_progress_95(self): """ test_validation_progress_95 Should fail Loan 12345 is 91 percent funded Loan 23456 is 77 percent funded """ self.filters['funding_progress'] = 95 try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 12345) # Invalid Exception except Exception: self.assertTrue(False) def test_validation_exclude_existing(self): """ test_validation_exclude_existing Should fail on loan 23456, which the user is already invested in. """ self.filters['exclude_existing'] = True try: self.filters.validate(self.loan_list) # Check the loan it failed on except FilterValidationError as e: self.assertEqual(e.loan['loan_id'], 23456) # Invalid Exception except Exception: self.assertTrue(False)
class TestLendingClub(unittest.TestCase): lc = None logger = None def setUp(self): self.logger = TestLogger() self.lc = LendingClub(logger=self.logger) self.lc.session.base_url = 'http://127.0.0.1:8000/' self.lc.session.set_logger(None) self.lc.authenticate('*****@*****.**', 'supersecret') # Make sure session is enabled and clear self.lc.session.post('/session/enabled') self.lc.session.request('delete', '/session') def tearDown(self): pass def test_cash_balance(self): cash = self.lc.get_cash_balance() self.assertEqual(cash, 216.02) def test_portfolios(self): portfolios = self.lc.get_portfolio_list() self.assertEqual(len(portfolios), 2) self.assertEqual(portfolios[0]['portfolioName'], 'Existing Portfolio') def test_build_portfolio(self): portfolio = self.lc.build_portfolio(200, 25, 15, 16) self.assertNotEqual(portfolio, False) self.assertEqual(portfolio['percentage'], 15.28) self.assertTrue('loan_fractions' in portfolio) self.assertEqual(len(portfolio['loan_fractions']), 15) def test_build_portfolio_session_fail(self): """ test_build_portfolio_session_fail" If the session isn't saved, fractions shouldn't be found, which should make the entire method return False """ # Disable session self.lc.session.post('/session/disabled') portfolio = self.lc.build_portfolio(200, 25, 15, 16) self.assertFalse(portfolio) def test_build_portfolio_no_match(self): """ test_build_portfolio_no_match" Enter a min/max percent that cannot match dummy returned JSON """ portfolio = self.lc.build_portfolio(200, 25, 17.6, 18.5) self.assertFalse(portfolio) def test_search(self): results = self.lc.search() self.assertTrue(results is not False) self.assertTrue('loans' in results) self.assertTrue(len(results['loans']) > 0)
import sys import unittest import getpass from random import choice from logger import TestLogger sys.path.insert(0, '.') sys.path.insert(0, '../') sys.path.insert(0, '../../') from lendingclub import LendingClub, Order from lendingclub.filters import Filter logger = TestLogger() lc = LendingClub(logger=logger) class LiveTests(unittest.TestCase): def setUp(self): # Clear any existing orders lc.session.clear_session_order() # Override Order.__place_order so that no orders can be made Order._Order__place_order = self.place_order_override # Make sure that the override worked o = Order(lc) self.assertEqual(o._Order__place_order('token'), 12345)
class AutoInvestor: """ Regularly check a LendingClub account for available cash and reinvest it automatically. """ lc = None authed = False verbose = False auto_execute = True settings = None loop = False app_dir = None # The file that the summary from the last investment is saved to last_investment_file = 'last_investment.json' def __init__(self, verbose=False, auto_execute=True): """ Create an AutoInvestor instance - Set verbose to True if you want to see debugging logs """ self.verbose = verbose self.auto_execute = auto_execute self.logger = util.create_logger(verbose) self.app_dir = util.get_app_directory() self.lc = LendingClub() # Set logger on lc if self.verbose: self.lc.set_logger(self.logger) # Create settings object self.settings = Settings(investor=self, settings_dir=self.app_dir, logger=self.logger, verbose=self.verbose) self.settings.investor = self # create a link back to this instance def version(self): """ Return the version number of the Lending Club Investor tool """ return util.get_version() def welcome_screen(self): print( "\n///--------------------------- $$$ ---------------------------\\\\\\" ) print( '| Welcome to the unofficial Lending Club investment tool |' ) print( " ---------------------------------------------------------------- \n" ) def get_auth(self): print( 'To start, we need to log you into Lending Club (your password will never be saved)\n' ) while True: self.settings.get_auth_settings() print('\nAuthenticating...') try: return self.authenticate() except Exception as e: print('\nLogin failed: {0}'.format(str(e.value))) print("Please try again\n") def setup(self): """ Setup the investor to run """ if self.verbose: print('VERBOSE OUTPUT IS ON\n') if not self.authed: self.get_auth() self.settings.select_profile() print('You have ${0} in your account, free to invest\n'.format( self.lc.get_cash_balance())) # Investment settings print('Now let\'s define what you want to do') # Use the settings from last time if self.settings.profile_loaded is not False: summary = self.settings.show_summary() if summary is False: # there was an error with saved settings print( '\nThere was an error with your saved settings. Please go through the prompts again.\n' ) self.settings.get_investment_settings() if util.prompt_yn('Would you like to use these settings?', 'y'): self.settings.save() # to save the email that was just entered else: self.settings.get_investment_settings() else: self.settings.get_investment_settings() # All ready to start running print( '\nThat\'s all we need. Now, as long as this is running, your account will be checked every {0} minutes and invested if enough funds are available.\n' .format(self.settings['frequency'])) def authenticate(self): """ Attempt to authenticate the user with the email/pass from the Settings object. This is just a wrapper for LendingClub.authenticate() Returns True or raises an exceptions """ self.authed = self.lc.authenticate(self.settings.auth['email'], self.settings.auth['pass']) return self.authed def run(self): """ Alias for investment_loop. This is used by python-runner """ self.investment_loop() def run_once(self): """ Try to invest, based on your settings, and then end the program. """ self.loop = False # Make sure the site is available attempts = 0 while not self.lc.is_site_available(): attempts += 1 if attempts % 5 == 0: self.logger.warn( 'LendingClub is not responding. Trying again in 10 seconds...' ) sleep(10) # Invest self.attempt_to_invest() def stop(self): """ Called when the investment loop should end. If the loop is currently attempting to invest cash, this will not be canceled. """ self.loop = False self.logger.info("Stopping investor...") def get_order_summary(self, portfolio): """ Log a summary of the investment portfolio which was ordered """ summary = 'Investment portfolio summary: {0} loan notes ('.format( portfolio['numberOfLoans']) breakdown = [] for grade in ['a', 'aa', 'b', 'c', 'd', 'e', 'f', 'g']: if portfolio[grade] > 0.0: percent = int(round(portfolio[grade])) breakdown.append('{0}:{1}%'.format(grade.upper(), percent)) if len(breakdown) > 0: summary += ', '.join(breakdown) summary += ')' return summary def attempt_to_invest(self): """ Attempt an investment if there is enough available cash and matching investment option Returns true if money was invested """ # Authenticate try: self.authenticate() self.logger.info('Authenticated') except Exception as e: self.logger.error('Could not authenticate: {0}'.format(e.value)) return False # Try to invest self.logger.info('Checking for funds to invest...') try: # Get current cash balance cash = self.lc.get_investable_balance() if cash > 0 and cash >= self.settings['min_cash']: # Invest self.logger.info( " $ $ $ $ $ $ $ $ $ $") # Create break in logs try: # Refresh saved filter filters = self.settings['filters'] if type(filters) is SavedFilter: filters.reload() # Find investment portfolio, starting will all your cash, # down to the minimum you're willing to invest # No more than 10 searches i = 0 portfolio = False decrement = None while portfolio is False and cash >= self.settings[ 'min_cash'] and i < 10: i += 1 # Try to find a portfolio try: self.logger.info( 'Searching for a portfolio for ${0}'.format( cash)) portfolio = self.lc.build_portfolio( cash, max_per_note=self.settings['max_per_note'], min_percent=self.settings['min_percent'], max_percent=self.settings['max_percent'], filters=filters, do_not_clear_staging=True) except LendingClubError as e: pass # Try a lower amount of cash to invest if not portfolio: self.logger.info( 'Could not find any matching portfolios for ${0}' .format(cash)) # Create decrement value that will search up to 5 more times if decrement is None: delta = cash - self.settings['min_cash'] if delta < 25: break elif delta <= 100: decrement = 25 else: decrement = delta / 4 # Just to be safe, shouldn't decrement in $10 increments if decrement < 10: break # We are at our lowest if cash <= self.settings['min_cash']: break # New amount to search for cash -= decrement if cash < self.settings['min_cash']: cash = self.settings['min_cash'] else: cash = util.nearest_25(cash) if portfolio: # Invest assign_to = self.settings['portfolio'] order = self.lc.start_order() order.add_batch(portfolio['loan_fractions']) if self.auto_execute: self.logger.info( 'Auto investing ${0} at {1}%...'.format( cash, portfolio['percentage'])) sleep(5) # last chance to cancel order._Order__already_staged = True # Don't try this at home kids order._Order__i_know_what_im_doing = True # Seriously, don't do it order_id = order.execute(portfolio_name=assign_to) else: self.logger.info( 'Order staged but not completed, please to go LendingClub website to complete the order. (see the "--no-auto-execute" command flag)' ) return False # Success! Show summary and save the order summary = self.get_order_summary(portfolio) self.logger.info(summary) self.logger.info('Done\n') self.save_last_investment(cash, portfolio, order_id, portfolio_name=assign_to) else: self.logger.warning( 'No investment portfolios matched your filters at this time -- Trying again in {2} minutes' .format(self.settings['min_percent'], self.settings['max_percent'], self.settings['frequency'])) except Exception as e: self.logger.exception( 'Failed trying to invest: {0}'.format(str(e))) else: self.logger.info( 'Only ${0} available for investing (of your ${1} balance)'. format(cash, self.lc.get_cash_balance())) return False except Exception as e: self.logger.error(str(e)) return False def save_last_investment(self, cash, portfolio, order_id, portfolio_name=None): """" Save a log of the last investment to the last_investment file """ try: last_invested = { 'timestamp': int(time.time()), 'order_id': order_id, 'portfolio': portfolio_name, 'cash': cash, 'investment': portfolio } # Convert to JSON json_out = json.dumps(last_invested) self.logger.debug( 'Saving last investment file with JSON: {0}'.format(json_out)) # Save file_path = os.path.join(self.app_dir, self.last_investment_file) f = open(file_path, 'w') f.write(json_out) f.close() except Exception as e: self.logger.warning( 'Couldn\'t save the investment summary to file (this warning can be ignored). {0}' .format(str(e))) def get_last_investment(self): """ Return the last investment summary that has been saved to the last_investment file """ try: file_path = os.path.join(self.app_dir, self.last_investment_file) if os.path.exists(file_path): # Read file f = open(file_path, 'r') json_str = f.read() f.close() # Convert to dictionary and return return json.loads(json_str) except Exception as e: self.logger.warning( 'Couldn\'t read the last investment file. {0}'.format(str(e))) return None def investment_loop(self): """ Start the investment loop Check the account every so often (default is every 60 minutes) for funds to invest The frequency is defined by the 'frequency' value in the ~/.lcinvestor/settings.yaml file """ self.loop = True frequency = self.settings.user_settings['frequency'] while self.loop: # Make sure the site is available (network could be reconnecting after sleep) attempts = 0 while not self.lc.is_site_available() and self.loop: attempts += 1 if attempts % 5 == 0: self.logger.warn( 'LendingClub is not responding. Trying again in 10 seconds...' ) sleep(10) # Invest self.attempt_to_invest() pause.minutes(frequency)
from random import choice PY3 = sys.version_info > (3,) if PY3: from builtins import input else: input = raw_input from logger import TestLogger from lendingclub import LendingClub, Order from lendingclub.filters import Filter logger = TestLogger() lc = LendingClub(logger=logger) class LiveTests(unittest.TestCase): def setUp(self): # Clear any existing orders lc.session.clear_session_order() # Override _place_order so that no orders can be made Order._place_order = self.place_order_override # Make sure that the override worked o = Order(lc) self.assertEqual(o._place_order('token'), 12345) def place_order_override(self, token):
class AutoInvestor: """ Regularly check a LendingClub account for available cash and reinvest it automatically. """ lc = None authed = False verbose = False auto_execute = True settings = None loop = False app_dir = None # The file that the summary from the last investment is saved to last_investment_file = 'last_investment.json' def __init__(self, verbose=False, auto_execute=True): """ Create an AutoInvestor instance - Set verbose to True if you want to see debugging logs """ self.verbose = verbose self.auto_execute = auto_execute self.logger = util.create_logger(verbose) self.app_dir = util.get_app_directory() self.lc = LendingClub() # Set logger on lc if self.verbose: self.lc.set_logger(self.logger) # Create settings object self.settings = Settings(investor=self, settings_dir=self.app_dir, logger=self.logger, verbose=self.verbose) self.settings.investor = self # create a link back to this instance def version(self): """ Return the version number of the Lending Club Investor tool """ return util.get_version(); def welcome_screen(self): print "\n///--------------------------- $$$ ---------------------------\\\\\\" print '| Welcome to the unofficial Lending Club investment tool |' print " ---------------------------------------------------------------- \n" def get_auth(self): print 'To start, we need to log you into Lending Club (your password will never be saved)\n' while True: self.settings.get_auth_settings() print '\nAuthenticating...' try: return self.authenticate() except Exception as e: print '\nLogin failed: {0}'.format(str(e.value)) print "Please try again\n" def setup(self): """ Setup the investor to run """ if self.verbose: print 'VERBOSE OUTPUT IS ON\n' if not self.authed: self.get_auth() self.settings.select_profile() print 'You have ${0} in your account, free to invest\n'.format(self.lc.get_cash_balance()) # Investment settings print 'Now let\'s define what you want to do' # Use the settings from last time if self.settings.profile_loaded is not False: summary = self.settings.show_summary() if summary is False: # there was an error with saved settings print '\nThere was an error with your saved settings. Please go through the prompts again.\n' self.settings.get_investment_settings() if util.prompt_yn('Would you like to use these settings?', 'y'): self.settings.save() # to save the email that was just entered else: self.settings.get_investment_settings() else: self.settings.get_investment_settings() # All ready to start running print '\nThat\'s all we need. Now, as long as this is running, your account will be checked every {0} minutes and invested if enough funds are available.\n'.format(self.settings['frequency']) def authenticate(self): """ Attempt to authenticate the user with the email/pass from the Settings object. This is just a wrapper for LendingClub.authenticate() Returns True or raises an exceptions """ self.authed = self.lc.authenticate(self.settings.auth['email'], self.settings.auth['pass']) return self.authed def run(self): """ Alias for investment_loop. This is used by python-runner """ self.investment_loop() def run_once(self): """ Try to invest, based on your settings, and then end the program. """ self.loop = False # Make sure the site is available attempts = 0 while not self.lc.is_site_available(): attempts += 1 if attempts % 5 == 0: self.logger.warn('LendingClub is not responding. Trying again in 10 seconds...') sleep(10) # Invest self.attempt_to_invest() def stop(self): """ Called when the investment loop should end. If the loop is currently attempting to invest cash, this will not be canceled. """ self.loop = False self.logger.info("Stopping investor...") def get_order_summary(self, portfolio): """ Log a summary of the investment portfolio which was ordered """ summary = 'Investment portfolio summary: {0} loan notes ('.format(portfolio['numberOfLoans']) breakdown = [] for grade in ['a', 'aa', 'b', 'c', 'd', 'e', 'f', 'g']: if portfolio[grade] > 0.0: percent = int(round(portfolio[grade])) breakdown.append('{0}:{1}%'.format(grade.upper(), percent)) if len(breakdown) > 0: summary += ', '.join(breakdown) summary += ')' return summary def attempt_to_invest(self): """ Attempt an investment if there is enough available cash and matching investment option Returns true if money was invested """ # Authenticate try: self.authenticate() self.logger.info('Authenticated') except Exception as e: self.logger.error('Could not authenticate: {0}'.format(e.value)) return False # Try to invest self.logger.info('Checking for funds to invest...') try: # Get current cash balance cash = self.lc.get_investable_balance() if cash > 0 and cash >= self.settings['min_cash']: # Invest self.logger.info(" $ $ $ $ $ $ $ $ $ $") # Create break in logs try: # Refresh saved filter filters = self.settings['filters'] if type(filters) is SavedFilter: filters.reload() # Find investment portfolio, starting will all your cash, # down to the minimum you're willing to invest # No more than 10 searches i = 0 portfolio = False decrement = None while portfolio is False and cash >= self.settings['min_cash'] and i < 10: i += 1 # Try to find a portfolio try: self.logger.info('Searching for a portfolio for ${0}'.format(cash)) portfolio = self.lc.build_portfolio(cash, max_per_note=self.settings['max_per_note'], min_percent=self.settings['min_percent'], max_percent=self.settings['max_percent'], filters=filters, do_not_clear_staging=True) except LendingClubError as e: pass # Try a lower amount of cash to invest if not portfolio: self.logger.info('Could not find any matching portfolios for ${0}'.format(cash)) # Create decrement value that will search up to 5 more times if decrement is None: delta = cash - self.settings['min_cash'] if delta < 25: break elif delta <= 100: decrement = 25 else: decrement = delta / 4 # Just to be safe, shouldn't decrement in $10 increments if decrement < 10: break # We are at our lowest if cash <= self.settings['min_cash']: break # New amount to search for cash -= decrement if cash < self.settings['min_cash']: cash = self.settings['min_cash'] else: cash = util.nearest_25(cash) if portfolio: # Invest assign_to = self.settings['portfolio'] order = self.lc.start_order() order.add_batch(portfolio['loan_fractions']) if self.auto_execute: self.logger.info('Auto investing ${0} at {1}%...'.format(cash, portfolio['percentage'])) sleep(5) # last chance to cancel order._Order__already_staged = True # Don't try this at home kids order._Order__i_know_what_im_doing = True # Seriously, don't do it order_id = order.execute(portfolio_name=assign_to) else: self.logger.info('Order staged but not completed, please to go LendingClub website to complete the order. (see the "--no-auto-execute" command flag)') return False # Success! Show summary and save the order summary = self.get_order_summary(portfolio) self.logger.info(summary) self.logger.info('Done\n') self.save_last_investment(cash, portfolio, order_id, portfolio_name=assign_to) else: self.logger.warning('No investment portfolios matched your filters at this time -- Trying again in {2} minutes'.format(self.settings['min_percent'], self.settings['max_percent'], self.settings['frequency'])) except Exception as e: self.logger.error('Failed trying to invest: {0}'.format(str(e))) else: self.logger.info('Only ${0} available for investing (of your ${1} balance)'.format(cash, self.lc.get_cash_balance())) return False except Exception as e: self.logger.error(str(e)) return False def save_last_investment(self, cash, portfolio, order_id, portfolio_name=None): """" Save a log of the last investment to the last_investment file """ try: last_invested = { 'timestamp': int(time.time()), 'order_id': order_id, 'portfolio': portfolio_name, 'cash': cash, 'investment': portfolio } # Convert to JSON json_out = json.dumps(last_invested) self.logger.debug('Saving last investment file with JSON: {0}'.format(json_out)) # Save file_path = os.path.join(self.app_dir, self.last_investment_file) f = open(file_path, 'w') f.write(json_out) f.close() except Exception as e: self.logger.warning('Couldn\'t save the investment summary to file (this warning can be ignored). {0}'.format(str(e))) def get_last_investment(self): """ Return the last investment summary that has been saved to the last_investment file """ try: file_path = os.path.join(self.app_dir, self.last_investment_file) if os.path.exists(file_path): # Read file f = open(file_path, 'r') json_str = f.read() f.close() # Convert to dictionary and return return json.loads(json_str) except Exception as e: self.logger.warning('Couldn\'t read the last investment file. {0}'.format(str(e))) return None def investment_loop(self): """ Start the investment loop Check the account every so often (default is every 60 minutes) for funds to invest The frequency is defined by the 'frequency' value in the ~/.lcinvestor/settings.yaml file """ self.loop = True frequency = self.settings.user_settings['frequency'] while self.loop: # Make sure the site is available (network could be reconnecting after sleep) attempts = 0 while not self.lc.is_site_available() and self.loop: attempts += 1 if attempts % 5 == 0: self.logger.warn('LendingClub is not responding. Trying again in 10 seconds...') sleep(10) # Invest self.attempt_to_invest() pause.minutes(frequency)