Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update tests and build process, test on latest and nightly #105

Merged
merged 4 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ os:
- osx
julia:
- 1.0
- latest
- nightly
notifications:
email: false
# uncomment the following lines to override the default test script
#script:
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
# - julia -e 'Pkg.clone(pwd()); Pkg.build("Pages"); Pkg.test("Pages"; coverage=true)'
git:
depth: 99999999

## uncomment the following lines to allow failures on nightly julia
## (tests will run but not make your overall status red)
matrix:
allow_failures:
- julia: nightly

## uncomment the following lines to override the default test script

after_success:
# push coverage results to Codecov
- julia -e 'using Pkg; cd(Pkg.dir("Currencies")); using Coverage; Codecov.submit(Codecov.process_folder())'
5 changes: 3 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ name = "Currencies"
uuid = "0fd90b74-7c1f-579e-9252-02cd883047b9"
license = "MIT"
authors = ["Eric Forgy <[email protected]>", "ScottPJones <[email protected]>"]
version = "0.13.0"
version = "0.14.0"

[extras]
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
build = ["JSON3"]
build = ["JSON3","Pkg"]
test = ["Test"]
132 changes: 38 additions & 94 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,39 @@
[![Build Status](https://travis-ci.org/JuliaFinance/Currencies.jl.svg?branch=master)](https://travis-ci.org/JuliaFinance/Currencies.jl)
[![Build status](https://ci.appveyor.com/api/projects/status/chnj7xc6r0deux92/branch/master?svg=true)](https://ci.appveyor.com/project/EricForgy/currencies-jl/branch/master)

This is a primordial package for the JuliaFinance ecosytem. It provides bare singleton types based on standard ISO 4167 currency codes to be used primarily for dispatch in other JuliaFinance packages together with three methods:
This is a primordial package for the JuliaFinance ecosytem. It provides bare singleton types based on standard ISO 4167 currency codes to be used primarily for dispatch in other JuliaFinance packages together with five methods:

- `name`: The name of the currency.
- `symbol`: The symbol of the currency.
- `currency`: The singleton type instance for a particular currency symbol
- `name`: The full name of the currency.
- `code`: The ISO 4167 code for the currency.
- `unit`: The minor unit, i.e. number of decimal places, for the currency.

Within JuliaFinance, currencies are defined in two separate packages:

- [Currencies.jl](https://github.com/JuliaFinance/Currencies.jl)
- [Instruments.jl](https://github.com/JuliaFinance/Instruments.jl)

A brief explanation and motivation for each is presented below.

## [Currencies.jl](https://github.com/JuliaFinance/Currencies.jl)

As mentioned, this package defines standard currencies as primordial singleton types that can be thought of as labels.

For example:

```julia
julia> using Currencies

julia> import Currencies: USD, EUR, JPY, IQD

julia> typeof(USD)
julia> typeof(currency(:USD))
Currency{:USD}

julia> sizeof(USD)
0

julia> for ccy in [USD, EUR, JPY, IQD]
println("Currency: $(ccy)")
println("Name: $(Currencies.name(ccy))")
println("Code: $(Currencies.code(ccy))")
println("Minor Unit: $(Currencies.unit(ccy))\n")
end
julia> for ccy in currency.([:USD, :EUR, :JPY, :IQD])
println("Currency: $(Currencies.symbol(ccy))")
println("Name: $(Currencies.name(ccy))")
println("Code: $(Currencies.code(ccy))")
println("Minor Unit: $(Currencies.unit(ccy))\n")
end

Currency: USD
Name: US Dollar
Expand All @@ -49,106 +58,41 @@ Code: 368
Minor Unit: 3
```

In finance, cash and currencies are a suprisingly difficult concept to get right.

Within the JuliaFinance ecosystem, currencies are defined in three packages with increasing complexity:

- [Currencies.jl](https://github.com/JuliaFinance/Currencies.jl)
- [FinancialInstruments.jl](https://github.com/JuliaFinance/FinancialInstruments.jl)
- [Positions.jl](https://github.com/JuliaFinance/Positions.jl)

A brief explanation and motivation for each is presented below.

## [Currencies.jl](https://github.com/JuliaFinance/Currencies.jl)

As mentioned, this package defines standard currencies as primordial singleton types that can be thought of conceptually as mere labels to build further upon. This package has no external dependencies other the Julia standard libraries.

If all you need is a list of currencies with names, ISO 4167 codes and minor units, e.g. for building a user interface, then this lightweight package is what you want.

## [FinancialInstruments.jl](https://github.com/JuliaFinance/FinancialInstruments.jl)

Moving up one notch in complexity, we have FinancialInstruments.jl.

A financial instrument is a tradeable monetary contract that creates an asset for some parties while, at same time, creating a liability for others.
If all you need is a list of currencies with names, ISO 4167 codes and minor units, e.g. for building a dropdown menu in a user interface, then this lightweight package is what you want.

Examples of financial instruments include stocks, bonds, loans, derivatives, etc. However, the most basic financial instruments are currencies.

If currencies are financial instruments, why not make `FinancialInstruments` the most primordial package and `Currencies` could simply extend `FinancialInstruments`?

That is certainly a tempting option, but, as mentioned above, there are use cases for currencies where the fact they are financial instruments is immaterial so it makes sense to have a lightweight primordial `Currencies` without the baggage.
## [Instruments.jl](https://github.com/JuliaFinance/Instruments.jl)

When a currency is thought of as a financial instrument (as opposed to a mere label used in UI component), we choose to refer to it as `Cash` (as it would appear in a balance sheet).

For example:

```julia
julia> using FinancialInstruments

julia> const FI = FinancialInstruments
FinancialInstruments
julia> import Instruments: USD

julia> FI.USD
Cash{Currency{:USD}}()
julia> typeof(USD)
Cash{:USD,2}
```

In this case, `Cash` is again a primordial singleton type, but other financial instruments can be `struct`s with various fields.

It is unlikely that as user will need `FinancialInstruments` directly. Rather, `FinancialInstruments` is intended for package developers as the base for building other, more complex, financial instruments.

## [Positions.jl](https://github.com/JuliaFinance/Positions.jl)
In this case, `Cash` is again a primordial singleton type although other financial instruments may contain various fields for cashflow projections and pricing.

Finally, we have `Positions.jl`.

A `Position` represents ownership of a financial instrument including the quantity of that financial instrument. For example, Microsoft stock (MSFT) is a financial instrument. A position could be 1,000 shares of MSFT.

In the case of currency, `FI.USD` would be a financial instrument and owning $1,000 would mean you own 1,000 units of the financial instrument `FI.USD`. Owning 1 unit of a currency, e.g. `FI.USD`, is a special position we denote simply (and with abuse of symbols) as `USD`.

If you are building a financial application that requires adding, subtracting, multiplying and dividing currencies, then you want to use `Positions`.

For example:
Instruments.jl also provides the `Position` type together with basic algebraic operations:

```julia
julia> using Positions

julia> import Positions: USD, JPY

julia> USD
1.00 USD
julia> import Instruments: USD, JPY

julia> 10USD
10.00 USD

julia> 10USD+20USD
30.00 USD

julia> 5*20USD
100.00 USD

julia> 100USD/5
20.00 USD

julia> 100USD/5USD
FixedDecimal{Int64,2}(20.00)
10.00USD

julia> 100JPY/5JPY
FixedDecimal{Int64,0}(20)
julia> typeof(10USD)
Position{Cash{:USD,2},FixedDecimal{Int64,2}}

julia> 100USD+100JPY
ERROR: promotion of types Position{Cash{Currency{:USD}},FixedPointDecimals.FixedDecimal{Int64,2}} and Position{Cash{Currency{:JPY}},FixedPointDecimals.FixedDecimal{Int64,0}} failed to change any arguments
julia> 10USD+20USD
30.00USD
```

Note that algebraic operations of currency positions require the positions to be of the same financial instrument. In this case, they must be the same currency as indicated by the error in the last command above.

## Summary

This package provides lightweight primordial definitions of standard currencies. It is intended for direct use only if the additional baggage of currency as a financial instrument is not required.

`FinancialInstruments` is an intermediate package that defines currencies as tradeable financial instruments as well as providing a base for extending to other more complex instruments.

`Positions` is a simple package that defines currency amounts and allows basic algebraic operations on currencies. If you are building a financial application, you probably want `Positions` as opposed to `Currencies`.

See also:
For more information, see

- [Instruments.jl](https://github.com/JuliaFinance/Instruments.jl)
- [Markets.jl](https://github.com/JuliaFinance/Markets.jl)
- [GeneralLedgers.jl](https://github.com/JuliaFinance/GeneralLedgers.jl)

Expand Down
1 change: 0 additions & 1 deletion REQUIRE

This file was deleted.

1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
environment:
matrix:
- julia_version: 1
- julia_version: latest
- julia_version: nightly

platform:
Expand Down
39 changes: 30 additions & 9 deletions deps/build.jl
Original file line number Diff line number Diff line change
@@ -1,32 +1,53 @@
import Pkg; Pkg.add("JSON3") # Just in case

using JSON3

# Data obtained from https://datahub.io/core/country-codes
inputname = joinpath(@__DIR__,"country-codes.json")
outputname = joinpath(@__DIR__, "currencies.jl")
const src = "https://pkgstore.datahub.io/core/country-codes/country-codes_json/data/471a2e653140ecdd7243cdcacfd66608/country-codes_json.json"

inputname = joinpath(@__DIR__, "country-codes.json")
outputname = joinpath(@__DIR__, "currency-data.jl")

# First, check if currency-data.jl already exists
isfile(outputname) && return

# Only download the file from datahub.io if not already present
if !isfile(inputname)
println("Downloading currency data: ", src)
download(src, inputname)
end

const country_list = open(io -> JSON3.read(io), inputname)

const SymAlpha = Symbol("ISO4217-currency_alphabetic_code")
const currency_list = Dict{String,Tuple{Int,Int,String}}()

const SymCurr = Symbol("ISO4217-currency_alphabetic_code")
const SymUnit = Symbol("ISO4217-currency_minor_unit")
const SymName = Symbol("ISO4217-currency_name")
const SymCode = Symbol("ISO4217-currency_numeric_code")

function genfile(io)
for country in country_list
(symlist = country[SymAlpha]) === nothing && continue
(abbrlist = country[SymCurr]) === nothing && continue
(unitlist = country[SymUnit]) === nothing && continue
(namelist = country[SymName]) === nothing && continue
(codelist = country[SymCode]) === nothing && continue
symbols = split(symlist, ',')
currencies = split(abbrlist, ',')
units = split(string(unitlist), ',')
names = split(namelist, ',')
codes = split(string(codelist), ',')

for (symbol, unit, code, name) in zip(symbols, units, codes, names)
length(symbol) != 3 && continue
println(io,"""Currency(:$symbol,$unit,$code,"$name")""")
for (curr, unit, code, name) in zip(currencies, units, codes, names)
length(curr) != 3 && continue
haskey(currency_list, curr) && continue
currency_list[curr] = (parse(Int, unit), parse(Int, code), string(name))
end
end
println(io, "const _currency_data = Dict(")
for (curr, val) in currency_list
println(io, " :$curr => (Currency{:$curr}(), $(val[1]), $(lpad(val[2], 4)), \"$(val[3])\"),")
end
println(io, ")\n")
end

open(io -> genfile(io), outputname, "w")

Loading