Skip to content

Instantly share code, notes, and snippets.

@marcamillion
Last active April 18, 2019 19:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcamillion/1cc97184e33a1cc65de2b5c622f79164 to your computer and use it in GitHub Desktop.
Save marcamillion/1cc97184e33a1cc65de2b5c622f79164 to your computer and use it in GitHub Desktop.
# == Schema Information
#
# Table name: port_stocks
#
# id :bigint(8) not null, primary key
# portfolio_id :integer
# stock_id :integer
# volume :integer
# transaction_price :float
# current_price :float
# percent_change :float
# created_at :datetime not null
# updated_at :datetime not null
# current_value :float
# dollar_change :float
# total_spend :float
# transaction_date :datetime
# action :integer
# position :integer default("open")
# ticker :string
# slug :string
#
class PortStock < ApplicationRecord
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
belongs_to :portfolio
belongs_to :stock
has_many :closed_positions, dependent: :destroy
accepts_nested_attributes_for :closed_positions, allow_destroy: true
has_many :order_histories, dependent: :destroy
enum action: [ :buy, :sell ]
enum position: [ :open, :closed ]
before_save :calculate_percent_change
before_save :calculate_dollar_change
before_save :calculate_current_value
before_save :calculate_total_spend
after_save :calculate_portfolio_initial_dollar_value
after_save :calculate_portfolio_num_winners
after_save :calculate_portfolio_num_losers
after_save :update_portfolio_current_dollar_value
after_save :update_portfolio_initial_dollar_value, if: (:total_spend_previously_changed? || :volume_previously_changed?)
after_save :calculate_portfolio_percent_change
after_save :calculate_portfolio_dollar_change
after_destroy :update_portfolio_values_on_delete
def slug_candidates
[
:ticker,
[:ticker, :volume],
[:ticker, :volume, :transaction_price],
[:ticker, :volume, :transaction_price, :created_at]
]
end
def calculate_percent_change
self.percent_change = ((self.current_price - self.transaction_price)/self.transaction_price)*100
end
def calculate_dollar_change
self.dollar_change = (self.current_price - self.transaction_price) * self.volume
end
def calculate_current_value
self.current_value = self.current_price * self.volume
end
def calculate_total_spend
self.total_spend = self.transaction_price * self.volume
end
# ========= Update Associated Portfolio Values
def calculate_portfolio_num_winners
self.portfolio.num_winners = self.portfolio.port_stocks.open.where("percent_change > 0").count
self.portfolio.save!
end
def calculate_portfolio_num_losers
self.portfolio.num_losers = self.portfolio.port_stocks.open.where("percent_change < 0").count
self.portfolio.save!
end
def update_portfolio_current_dollar_value
self.portfolio.current_dollar_value = self.portfolio.port_stocks.open.map(&:current_value).sum
self.portfolio.save!
end
def update_portfolio_initial_dollar_value
self.portfolio.initial_dollar_value = self.portfolio.port_stocks.open.map { |ps| ps.volume * ps.transaction_price }.sum
self.portfolio.save!
end
def update_portfolio_values_on_delete
if self.portfolio.port_stocks.open.count > 0
self.portfolio.current_dollar_value = self.portfolio.port_stocks.open.map(&:current_value).sum
self.portfolio.initial_dollar_value = self.portfolio.port_stocks.open.map { |ps| ps.volume * ps.transaction_price }.sum
self.portfolio.dollar_change = self.portfolio.current_dollar_value - self.portfolio.initial_dollar_value
self.portfolio.percent_change = ((self.portfolio.current_dollar_value - self.portfolio.initial_dollar_value)/self.portfolio.initial_dollar_value)*100
self.portfolio.num_winners, self.portfolio.num_losers = self.portfolio.port_stocks.open.where("percent_change > 0").count, self.portfolio.port_stocks.open.where("percent_change < 0").count
self.portfolio.save!
else
self.portfolio.current_dollar_value, self.portfolio.initial_dollar_value, self.portfolio.dollar_change, self.portfolio.percent_change = 0.0, 0.0, 0.0, 0.0
self.portfolio.num_winners, self.portfolio.num_losers = 0, 0
self.portfolio.save!
end
end
def calculate_portfolio_initial_dollar_value
if self.portfolio.initial_dollar_value.nil?
self.portfolio.initial_dollar_value = 0.0
end
self.portfolio.initial_dollar_value += (self.transaction_price * self.volume)
self.portfolio.save!
end
def calculate_portfolio_percent_change
self.portfolio.percent_change = ((self.portfolio.current_dollar_value - self.portfolio.initial_dollar_value)/self.portfolio.initial_dollar_value)*100
self.portfolio.save!
end
def calculate_portfolio_dollar_change
self.portfolio.dollar_change = self.portfolio.current_dollar_value - self.portfolio.initial_dollar_value
self.portfolio.save!
end
end
# == Schema Information
#
# Table name: port_stocks
#
# id :bigint(8) not null, primary key
# portfolio_id :integer
# stock_id :integer
# volume :integer
# transaction_price :float
# current_price :float
# percent_change :float
# created_at :datetime not null
# updated_at :datetime not null
# current_value :float
# dollar_change :float
# total_spend :float
# transaction_date :datetime
# action :integer
# position :integer default("open")
# ticker :string
# slug :string
#
require 'rails_helper'
RSpec.describe PortStock, type: :model do
let(:stock) { create(:stock, price: 10.00) }
let(:portfolio) { create(:portfolio) }
let(:port_stock_1) { create(:port_stock, stock: stock, portfolio: portfolio, transaction_price: stock.price, action: :buy, volume: 100) }
context "associations" do
it { should belong_to(:portfolio) }
it { should belong_to (:stock) }
end
context "methods" do
it "should accurately calculate the positive percent_change of the current PortStock" do
port_stock_1.current_price = 20.00
expect(port_stock_1.calculate_percent_change).to eql 100.00
end
it "should accurately calculate the negative percent_change of the current PortStock" do
port_stock_1.current_price = 5.00
expect(port_stock_1.calculate_percent_change).to eql(-50.00)
end
it "should accurately calculate the positive dollar_change of the current PortStock" do
port_stock_1.current_price = 15.00
port_stock_1.volume = 1000
expect(port_stock_1.calculate_dollar_change).to eql 5000.00
end
it "should accurately calculate the negative dollar_change of the current PortStock" do
port_stock_1.current_price = 5.00
port_stock_1.volume = 1000
expect(port_stock_1.calculate_dollar_change).to eql (-5000.00)
end
it "should accurately calculate the current_value of the PortStock when the price increases" do
port_stock_1.current_price = 15.00
port_stock_1.volume = 1000
expect(port_stock_1.calculate_current_value).to eql 15000.00
end
it "should accurately calculate the negative current_value of the PortStock when the price decreases" do
port_stock_1.current_price = 5.00
port_stock_1.volume = 1000
expect(port_stock_1.calculate_current_value).to eql 5000.00
end
it "should accurately calculate the total_spend of the current PortStock on original purchase" do
expect(port_stock_1.calculate_total_spend).to eql 1000.00
end
it "should accurately calculate the portfolio's initial_dollar_value" do
binding.pry
expect(portfolio.initial_dollar_value).to eql 1000.00
end
it "should accurately calculate the portfolio's number of winners"
it "should accurately calculate the portfolio's number of losers"
it "should accurately update the portfolio's current_dollar_value"
it "should accurately update the portfolio's initial_dollar_value"
it "should accurately calculate the portfolio's percent_change"
it "should accurately calculate the portfolio's dollar_change"
it "should accurately update all portfolio values when PortStock is deleted"
end
context "callbacks" do
it "should accurately calculate the percent_change of the current PortStock BEFORE SAVING"
it "should accurately calculate the dollar_change of the current PortStock BEFORE SAVING"
it "should accurately calculate the current_value of the current PortStock BEFORE SAVING"
it "should accurately calculate the total_spend of the current PortStock BEFORE SAVING"
it "should accurately calculate the portfolio's initial_dollar_value AFTER SAVING"
it "should accurately calculate the portfolio's number of winners AFTER SAVING"
it "should accurately calculate the portfolio's number of losers AFTER SAVING"
it "should accurately update the portfolio's current_dollar_value AFTER SAVING"
it "should accurately update the portfolio's initial_dollar_value AFTER SAVING"
it "should accurately calculate the portfolio's percent_change AFTER SAVING"
it "should accurately calculate the portfolio's dollar_change AFTER SAVING"
it "should accurately update all portfolio values when PortStock is deleted"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment