Skip to content

Commit

Permalink
@cash and @stock return position types
Browse files Browse the repository at this point in the history
  • Loading branch information
Eric Forgy authored and Eric Forgy committed Jul 4, 2020
1 parent 23206aa commit cced416
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 65 deletions.
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ keywords = ["Finance", "Asset"]
license = "MIT"
desc = "Financial assets"
authors = ["Eric Forgy <[email protected]>", "ScottPJones <[email protected]>"]
version = "0.9.0"
version = "0.10.0"

[deps]
Currencies = "0fd90b74-7c1f-579e-9252-02cd883047b9"
FixedPointDecimals = "fb4d412d-6eee-574d-9565-ede6634db7b0"
Instruments = "2a4f3d17-849a-48a1-809e-d780c70a95a0"

[compat]
Currencies = ">= 0.17"
Instruments = ">= 0.8"
Currencies = "0.18"
Instruments = "0.9"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
79 changes: 31 additions & 48 deletions src/Assets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,83 +17,66 @@ Licensed under MIT License, see LICENSE.md
module Assets

using Currencies, FixedPointDecimals, Instruments
import Currencies: symbol, currency, unit, code, name
import Currencies: unit, code, name
import Instruments: Position

export Cash, cash, @cash_str, @cash
export Stock, stock, @stock_str, @stock
export Position, Currency, Cash, cash, @cash
export Stock, stock, @stock

"""
`Cash` is an implementation of `Instrument` represented by a singleton type,
with its currency symbol and the number of digits in the minor units,
typically 0, 2, or 3, as parameters.
"""
"`Cash` is an implementation of `Instrument` represented by a singleton type, with its currency symbol and the number of digits in the minor units, typically 0, 2, or 3, as parameters."
struct Cash{S, N} <: Instrument{S,Currency{S}}
function Cash{S,N}() where {S,N}
unit(S) === N || error("Currency minor unit does not match.")
new{S,N}()
end
end

# Functions to return the types, not instances, with the constructors
cash(S::Symbol) = Cash{S,unit(S)}
cash(::Type{Currency{S}}) where {S} = cash(S)
function Position(::Type{I},amt) where {S,N,I<:Cash{S,N}}
T = FixedDecimal{Int,N}
Position{I,T}(T(amt))
end

# Handle using the type instead of an instance for Cash
symbol(::Type{<:Cash{S}}) where {S} = S
unit(::Type{Cash{S,N}}) where {S,N} = N
code(::Type{<:Cash{S}}) where {S} = code(S)
name(::Type{<:Cash{S}}) where {S} = name(S)
currency(::Type{<:Cash{S}}) where {S} = currency(S)
"Return a cash instrument type."
function cash end

macro cash_str(str)
:( cash(Symbol($(esc(uppercase(str))))) )
end
cash(S::Symbol) = Cash{S,unit(S)}

cash(::Type{Currency{S}}) where {S} = cash(S)

macro cash(syms)
args = syms isa Expr ? syms.args : [syms]
for nam in args
lownam = Symbol(lowercase(string(nam)))
Base.eval(__module__, :( const $nam = cash($(QuoteNode(nam))) ) )
Base.eval(__module__, :( const $lownam = $nam() ) )
@eval __module__ const $nam = Assets.$nam
end
end

function Position{S}(amt) where {C,N,S<:Cash{C,N}}
T = FixedDecimal{Int,N}
Position{S,T}(T(amt))
end
unit(::Type{Cash{S,N}}) where {S,N} = N

"""
`Stock` is an implementation of a simple `Instrument` represented by a singleton type
with a stock symbol and currency, e.g. `Stock{:MSFT,ccy"USD"}`.
The currency can be omitted and it will default to USD, e.g. `Stock(:MSFT)`.
"""
struct Stock{S,C} <: Instrument{S,Currency}
Stock(sym::Symbol, ccy::Type{<:Currency}=Currency(:USD)) = new{sym,currency(ccy)}()
Stock(sym::Symbol, pos::Type{<:Cash}) = new{sym,currency(pos)}()
end
stock(sym::Symbol, ccy::Type{<:Currency}=Currency(:USD)) = typeof(Stock(sym, currency(ccy)))
stock(sym::Symbol, pos::Type{<:Cash}) = typeof(Stock(sym, currency(pos)))
code(::Type{C}) where {S,C<:Cash{S}} = code(S)

name(::Type{C}) where {S,C<:Cash{S}} = name(S)

symbol(::Type{Stock{S,C}}) where {S,C} = S
currency(::Type{Stock{S,C}}) where {S,C} = C
"""`Stock` is an implementation of a simple `Instrument` represented by a singleton type with a stock symbol and currency, e.g. `Stock{:MSFT,ccy"USD"}`. The currency can be omitted and it will default to USD, e.g. `Stock(:MSFT)`."""
struct Stock{S,C} <: Instrument{S,C} end

macro stock_str(str, ccy="USD")
:( stock(Symbol($(esc(str))), $(esc(currency(Symbol(uppercase(ccy))))) ) )
function Position(::Type{I},amt) where {I<:Stock}
Position{I,Int}(amt)
end

Stock(S::Symbol, C::Type{<:Currency}=Currency{:USD}) = Stock{S,C}()

stock(S::Symbol, C::Type{<:Currency}=Currency{:USD}) = Stock{S,C}

macro stock(syms)
args = syms isa Expr ? syms.args : [syms]
for nam in args
lownam = Symbol(lowercase(string(nam)))
Base.eval(__module__, :( const $nam = stock($(QuoteNode(nam))) ) )
Base.eval(__module__, :( const $lownam = $nam() ) )
@eval __module__ const $nam = typeof(Position(Stock{$(QuoteNode(nam)),Currency{:USD}},0))
end
end

function Position{S}(amt) where {S<:Stock}
T = FixedDecimal{Int,4}
Position{S,T}(T(a))
# Contruct cash position types for convenience.
for (s,(ccy,u,c,n)) in Currencies.allpairs()
@eval const $s = typeof(Position($(cash(ccy)),0))
end

end # module Assets
30 changes: 16 additions & 14 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Assets, Currencies, Instruments, FixedPointDecimals
using Currencies: currency, symbol, unit, code, name
# using Currencies: currency, symbol, unit, code, name

@cash USD, EUR, JPY, JOD, CNY

Expand All @@ -14,7 +14,8 @@ currencies = ((USD, :USD, 2, 840, "US Dollar"),
(CNY, :CNY, 2, 156, "Yuan Renminbi"))

@testset "Basic currencies" begin
for (ccy, s, u, c, n) in currencies
for (pos, s, u, c, n) in currencies
ccy = currency(pos(1))
@test symbol(ccy) == s
@test unit(ccy) == u
@test name(ccy) == n
Expand All @@ -24,24 +25,25 @@ end

@testset "All currencies" begin
for sym in Currencies.allsymbols()
ccy = Currency{sym}()
ccy = Currency{sym}
ct = cash(sym)
@test ct == cash(typeof(ccy))
@test ct == cash(ccy)
@test ct == cash(sym)
@test currency(ct) == typeof(ccy)
@test currency(ct) == ccy
@test symbol(ct) == symbol(ccy)
@test unit(ct) == unit(ccy)
@test code(ct) == code(ccy)
@test name(ct) == name(ccy)

position = Position{ct}(1)
@test currency(position) == currency(ct)
@test currency(1ct) == typeof(ccy)
@test 1ct == position
@test ct * 1 == position
@test 1ct + 1ct == Position{ct}(2)
@test 1ct - 1ct == Position{ct}(0)
@test 20ct / 4ct == FixedDecimal{Int,unit(ct)}(5)
@test 20ct / 4 == Position{ct}(5)
pos = Position(ct,1)
pt = typeof(pos)
@test currency(pos) == currency(ct)
@test currency(1pt) == ccy
@test 1pt == pos
@test pos * 1 == pos
@test 1pos + 1pos == Position(ct,2)
@test 1pos - 1pos == Position(ct,0)
@test 20pos / 4pos == FixedDecimal{Int,unit(ct)}(5)
@test 20pos / 4 == Position(ct,5)
end
end

0 comments on commit cced416

Please sign in to comment.