diff --git a/Project.toml b/Project.toml index de6a24c..de3295f 100644 --- a/Project.toml +++ b/Project.toml @@ -4,7 +4,7 @@ keywords = ["Finance", "Asset"] license = "MIT" desc = "Financial assets" authors = ["Eric Forgy ", "ScottPJones "] -version = "0.9.0" +version = "0.10.0" [deps] Currencies = "0fd90b74-7c1f-579e-9252-02cd883047b9" @@ -12,8 +12,8 @@ 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" diff --git a/src/Assets.jl b/src/Assets.jl index 763f882..a6b006a 100644 --- a/src/Assets.jl +++ b/src/Assets.jl @@ -17,16 +17,13 @@ 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.") @@ -34,66 +31,52 @@ struct Cash{S, N} <: Instrument{S,Currency{S}} 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 diff --git a/test/runtests.jl b/test/runtests.jl index c7651bb..f0f3d92 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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 @@ -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 @@ -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