<commit_before>#!/usr/bin/ruby

require 'yinum'
require 'rest-client'
require 'json'
require 'pp'
require 'washbullet'
require 'byebug'

#  TODO:
#  Exclude loans already invested in
#  Implement PushBullit notifications

# LendingClub Configurations #
$debug = true
$authorization = ''
$account = 2628791
$accountURL = "/accounts/#{$account}"
$apiVersion = '/v1'
$baseURL = "https://api.lendingclub.com/api/investor#{$apiVersion}"
$contentType = 'application/json'

$portfolioId = 59612258  	# Autoinvestor
$investmentAmount = 25 		# amount to invest per loan ($25 minimum)

#PushBullet
$pushbulletApiKey = ''
$deviceId = '' #iphone 5s


TERMS = Enum.new(:TERMS, :months60 => 60, :months36 => 36)
PURPOSES = Enum.new(:PURPOSES, :credit_card_refinancing => 'credit_card_refinance', :consolidate => 'debt_consolidation', :other => 'other', :credit_card => 'credit_card', :home_improvement => 'home_improvement', :small_business => 'small_business')

class Account

	def availableCash
		@availableCash ||= Account.GetAvailableCash
	end

	def self.GetAvailableCash
		methodURL = "#{$baseURL}#{$accountURL}/availablecash"
		if $debug
			puts "methodURL: #{__method__} -> #{methodURL}"
		end

		response = RestClient.get(methodURL,
			"Authorization" => $authorization,
			"Accept" => $contentType,
			"Content-Type" => $contentType
		)
		
		result = JSON.parse(response)['availableCash']
		PBC.addLine("Available Cash:  #{result}")
		return result
	end
end


class Loans
	
	def availableLoans
		@availableLoans ||= Loans.GetAvailableLoans
	end

	def self.GetAvailableLoans
		methodURL = "#{$baseURL}/loans/listing"
		if $debug
			puts "Pulling fresh Loans data."
			puts "methodURL: #{__method__} -> #{methodURL}"
		end

		response = RestClient.get( methodURL, 
			"Authorization" => $authorization,
			"Accept" => $contentType,
			"Content-Type" => $contentType
		)
		
		result = JSON.parse(response)
		PBC.addLine("Pre-Filtered Loan Count:  #{result.values[1].size}")
		return result 
	end	

	def filterLoans 
		@availableLoans = @availableLoans.values[1].select do |o|
			o["term"].to_i == TERMS.months36 && 
			 o["annualInc"].to_i / 12 > 3000 &&
			 o["empLength"].to_i > 23 &&
			 o["inqLast6Mths"].to_i == 1 &&
			 o["pubRec"].to_i == 0 &&
			 o["intRate"].to_f < 27.0 &&
			o["intRate"].to_f > 15.5 &&
			o["dti"].to_i <= 20 &&
			o["delinq2Yrs"].to_i < 4 &&
			( 	# exclude loans where the installment amount is more than 10% of the borrowers monthly income
				o["installment"].to_f / (o["annualInc"].to_f / 12) < 0.1 
			) &&
			(
				o["purpose"].to_s == PURPOSES.credit_card || 
				o["purpose"].to_s == PURPOSES.credit_card_refinancing ||
				o["purpose"].to_s == PURPOSES.consolidate
			)
		end
		@availableLoans.sort! { |a,b| b["intRate"].to_f <=> a["intRate"].to_i }
	 	
	 	if $debug
	 		pp @availableLoans 
	 	end
		
		PBC.addLine("Post-Filtered Loan Count:  #{@availableLoans.size}")
	end

	def buildOrderList
		purchasableLoanCount = [Loans.PurchasableLoanCount, @availableLoans.size].min 

		PBC.addLine("Attempting to purchas #{purchasableLoanCount } loans.")

		if purchasableLoanCount > 0
			@Orders = Hash["aid" => $account, "orders" => 
				@availableLoans.first(Loans.PurchasableLoanCount).map do |o|
					Hash[
							'loanId' => o["id"].to_i,
						 	'requestedAmount' => $investmentAmount, 
						 	'portfolioId' => $portfolioId
						]
				end
			]
		end
	end

	def self.PurchasableLoanCount
		A.availableCash.to_i / $investmentAmount 
	end

	def placeOrder

		if @Orders == nil
			puts "No loans to purchase."
			return
		else
		 	methodURL = "#{$baseURL}#{$accountURL}/orders"
		 	if $debug
		 		puts "methodURL: #{__method__} -> #{methodURL}"
		 	end

		 	#response = {}
		 	response = RestClient.post(methodURL, @Orders.to_json,
		 		"Authorization" => $authorization,
		 		"Accept" => $contentType,
		 		"Content-Type" => $contentType
		 		)

			PBC.addLine "Successfully Invested: $#{response.values[1].select { |o| o["executionStatus"].include? 'ORDER_FULFILLED' }.inject(0) { |sum, o| sum + o["investedAmount"] } }"
	 	end
	end
end


class PushBullet
	def initialize
		@client ||= PushBullet.initializePushBulletClient
	end

	def self.initializePushBulletClient
		Washbullet::Client.new($pushbulletApiKey)
	end

	def addLine(line)
		@message = "#{@message}\r\n#{line}"
	end
	
	def sendMessage
	#	@client.push_note($deviceId, , params: { title: 'Lending Club AutoInvestor Update', body: @message } )
	end

	def viewMessage
		puts @message
	end
end


A = Account.new
L = Loans.new
PBC = PushBullet.new	

A.availableCash
L.availableLoans
L.filterLoans
L.buildOrderList
L.placeOrder
puts "PCB Message:"
PBC.viewMessage
#PBC.sendMessage

<commit_msg>Full implementation of PushBullet notifications and a bit of refactoring.<commit_after>#!/usr/bin/ruby

require 'yinum'
require 'rest-client'
require 'json'
require 'pp'
require 'washbullet'
require 'byebug'

#  TODO:
#  Exclude loans already invested in


# LendingClub Configurations #
$debug = true
$authorization = ''
$account = 2628791
$accountURL = "/accounts/#{$account}"
$apiVersion = '/v1'
$baseURL = "https://api.lendingclub.com/api/investor#{$apiVersion}"
$contentType = 'application/json'

$portfolioId = 59612258  	# Autoinvestor
$investmentAmount = 25 		# amount to invest per loan ($25 minimum)

#PushBullet
$pushbulletApiKey = ''
$deviceId = ''



class Account

	def availableCash
		availableCash ||= Account.GetAvailableCash
	end

	def self.GetAvailableCash
		methodURL = "#{$baseURL}#{$accountURL}/availablecash"
		if $debug
			puts "Pulling list of available loans since last release."
			puts "methodURL: #{__method__} -> #{methodURL}"
		end

		response = RestClient.get(methodURL,
			"Authorization" => $authorization,
			"Accept" => $contentType,
			"Content-Type" => $contentType
		)
		
		result = JSON.parse(response)['availableCash']
		PB.addLine("Available Cash:  #{result}")
		return result
	end
end


class Loans
	
	TERMS = Enum.new(:TERMS, :months60 => 60, :months36 => 36)
	PURPOSES = Enum.new(:PURPOSES, :credit_card_refinancing => 'credit_card_refinance', :consolidate => 'debt_consolidation', :other => 'other', :credit_card => 'credit_card', :home_improvement => 'home_improvement', :small_business => 'small_business')

	def purchasLoans
		filterLoans(loanList)
		placeOrder(buildOrderList)
		PB.sendMessage # send PushBullet message
	end

	def loanList
		@loanList ||= Loans.GetAvailableLoans
	end

	def self.GetAvailableLoans
		methodURL = "#{$baseURL}/loans/listing"
		if $debug
			puts "Pulling fresh Loans data."
			puts "methodURL: #{__method__} -> #{methodURL}"
		end

		response = RestClient.get( methodURL, 
			"Authorization" => $authorization,
			"Accept" => $contentType,
			"Content-Type" => $contentType
		)
		
		result = JSON.parse(response)
		PB.addLine("Pre-Filtered Loan Count:  #{result.values[1].size}")
		return result 
	end	 

	def filterLoans(ll)
		@loanList = ll.values[1].select do |o|
			o["term"].to_i == TERMS.months36 && 
			o["annualInc"].to_i / 12 > 3000 &&
			o["empLength"].to_i > 23 &&
			o["inqLast6Mths"].to_i == 1 &&
			o["pubRec"].to_i == 0 &&
			o["intRate"].to_f < 27.0 &&
			o["intRate"].to_f > 15.5 &&
			o["dti"].to_i <= 20 &&
			o["delinq2Yrs"].to_i < 4 &&
			( 	# exclude loans where the installment amount is more than 10% of the borrower's monthly income
				o["installment"].to_f / (o["annualInc"].to_f / 12) < 0.1 
			) &&
			(
				o["purpose"].to_s == PURPOSES.credit_card || 
				o["purpose"].to_s == PURPOSES.credit_card_refinancing ||
				o["purpose"].to_s == PURPOSES.consolidate
			)
		end
		
		@loanList.sort! { |a,b| b["intRate"].to_f <=> a["intRate"].to_i }
	 	
	 	if $debug
	 		puts "Filtered Loan List:"
	 		pp @loanList 	 	
	 	end

		PB.addLine("Post-Filtered Loan Count:  #{@loanList.size}")
	end

	def buildOrderList
		purchasableLoanCount = [Loans.PurchasableLoanCount, @loanList.size].min 

		PB.addLine("Attempting to purchas #{purchasableLoanCount} loans.")

		if purchasableLoanCount > 0
			orderList = Hash["aid" => $account, "orders" => 
				@loanList.first(Loans.PurchasableLoanCount).map do |o|
					Hash[
							'loanId' => o["id"].to_i,
						 	'requestedAmount' => $investmentAmount, 
						 	'portfolioId' => $portfolioId
						]
				end
			]
		end
		return orderList
	end

	def self.PurchasableLoanCount
		A.availableCash.to_i / $investmentAmount 
	end

	def placeOrder(orderList)

		if orderList != nil
		 	methodURL = "#{$baseURL}#{$accountURL}/orders"
		 	if $debug
		 		puts "Placing purchas order."
		 		puts "methodURL: #{__method__} -> #{methodURL}"
		 	end

		 	response = RestClient.post(methodURL, orderList.to_json,
		 		"Authorization" => $authorization,
		 		"Accept" => $contentType,
		 		"Content-Type" => $contentType
		 		)
			
			PB.setSubject "#{response.values[1].select { |o| o["executionStatus"].include? 'ORDER_FULFILLED' }.size} of #{Loans.PurchasableLoanCount}"
			PB.addLine "Successfully Invested: $#{response.values[1].select { |o| o["executionStatus"].include? 'ORDER_FULFILLED' }.inject(0) { |sum, o| sum + o["investedAmount"] } }"
	 	else
	 		PB.setSubject "0 of #{Loans.PurchasableLoanCount}"
	 	end
	end
end


class PushBullet
	def initialize
		@client ||= PushBullet.initializePushBulletClient
		addLine(Time.now.strftime("%H:%M %d/%m/%Y"))
	end

	def self.initializePushBulletClient
		Washbullet::Client.new($pushbulletApiKey)
	end

	def addLine(line)
		@message = "#{@message}\r\n#{line}"
	end
	
	def setSubject(purchasCount)
		@subject = "Lending Club AutoInvestor - #{purchasCount} purchases"
	end

	def sendMessage
		if $debug
	 		puts "PushBullet Message:"
	 		puts viewMessage
	 	end
		@client.push_note(receiver: $deviceId, params: { title: @subject, body: @message } )
		@client = nil
	end

	def viewMessage
		puts "Subject:  #{@subject}"
		puts "Message:  #{@message}"
	end
end


A = Account.new
L = Loans.new
PB = PushBullet.new	


L.purchasLoans


{'fraction_non_alphanumeric': 0.14646975520938701, 'fraction_numerical': 0.012846449524580214, 'mean_word_length': 3.9213539074166253, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 63, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2918830', 'n_tokens_mistral': 4174, 'n_tokens_neox': 3560, 'n_words': 743}