Skip to content
This repository was archived by the owner on Sep 6, 2019. It is now read-only.

Commit a47d751

Browse files
Add tests for Money, ExchangeRate and ExchangeRatManager
1 parent 89ecaf5 commit a47d751

File tree

6 files changed

+157
-52
lines changed

6 files changed

+157
-52
lines changed

src/currencies/exchangerate.jl

+17-21
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ immutable ExchangeRate
66
rateChain::(ExchangeRate, ExchangeRate)
77

88
ExchangeRate(source::Currency, target::Currency, rate::Float64)=new(source, target, rate, :Direct)
9+
ExchangeRate(source::Currency, target::Currency, rate::Float64, type_::Symbol, rateChain::(ExchangeRate, ExchangeRate))=new(source, target, rate, type_, rateChain)
910
end
1011

1112
# Apply the exchange rate to a cash amount
@@ -22,7 +23,6 @@ function exchange(ER::ExchangeRate, m::Money)
2223
end
2324

2425
elseif ER.type_==:Derived #Derived from exchange rates between other currencies
25-
2626
if m.currency==ER.rateChain[1].source || m.currency==ER.rateChain[1].target
2727
return exchange(ER.rateChain[2], exchange(ER.rateChain[1], m))
2828
elseif m.currency==ER.rateChain[2].source || m.currency==ER.rateChain[2].target
@@ -37,39 +37,35 @@ end
3737

3838
# Chain two exchange rates
3939
function chain(ER1::ExchangeRate, ER2::ExchangeRate)
40-
41-
result::ExchangeRate
42-
result.type_=:Derived
43-
result.rateChain=(ER1, ER2)
44-
40+
4541
if ER1.source==ER2.source
4642

47-
result.source=ER1.target
48-
result.target=ER2.target
49-
result.rate=ER2.rate/ER1.rate
43+
source=ER1.target
44+
target=ER2.target
45+
rate=ER2.rate/ER1.rate
5046

5147
elseif ER1.source==ER2.target
5248

53-
result.source=ER1.target
54-
result.target=ER2.source
55-
result.rate=1.0/(ER1.rate*ER2.rate)
49+
source=ER1.target
50+
target=ER2.source
51+
rate=1.0/(ER1.rate*ER2.rate)
5652

5753
elseif ER1.target==ER2.source
5854

59-
result.source=ER1.source
60-
result.target=ER2.target
61-
result.rate=ER1.rate*ER2.rate
55+
source=ER1.source
56+
target=ER2.target
57+
rate=ER1.rate*ER2.rate
6258

6359
elseif ER1.target==ER2.target
6460

65-
result.source=ER1.source
66-
result.target=ER2.source
67-
result.rate=ER1.rate/ER2.rate
61+
source=ER1.source
62+
target=ER2.source
63+
rate=ER1.rate/ER2.rate
6864

6965
else
7066
error("Exchange rates not chainable")
7167
end
7268

73-
return result
74-
75-
end
69+
return ExchangeRate(source, target, rate, :Derived, (ER1, ER2))
70+
end
71+

src/currencies/exchangeratemanager.jl

+21-15
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function add(ERM::ExchangeRateManager, ER::ExchangeRate, startdate::CalendarTime
2121
if !haskey(ERM.data, key)
2222
ERM.data[key]=Array(Entry, 0)
2323
end
24-
push!(ERM.data[key], Entry(ER, startdate, enddate))
24+
push!(ERM.data[key], Entry(ER, startdate, enddate))
2525
end
2626

2727
function addKnownRates(ERM::ExchangeRateManager)
@@ -53,14 +53,17 @@ function addKnownRates(ERM::ExchangeRateManager)
5353
end
5454

5555
function clear(ERM::ExchangeRateManager)
56-
ERM.data=Dict{BigInt, Array{Entry}}() # Delete the dictionary
57-
addKnownRates(ERM)
56+
ERM=ExchangeRateManager()
5857
end
5958

6059
hash(c1::Currency, c2::Currency)=BigInt(min(c1.numeric, c2.numeric))*1000+BigInt(max(c1.numeric, c2.numeric))
61-
hashes(key::BigInt, c::Currency)=c.numeric==key%1000 || c.numeric==key/1000
60+
hashes(key::BigInt, c::Currency)=c.numeric==key%1000 || c.numeric==trunc(key/1000)
6261

6362
function fetch(ERM::ExchangeRateManager, source::Currency, target::Currency, date::CalendarTime)
63+
if !haskey(ERM.data, hash(source, target))
64+
return nothing
65+
end
66+
6467
entries=ERM.data[hash(source, target)]
6568
for entry in entries
6669
if date>=entry.start && date<=entry.end_
@@ -80,7 +83,7 @@ function directLookup(ERM::ExchangeRateManager, source::Currency, target::Curren
8083
end
8184
end
8285

83-
function smartLookup(ERM::ExchangeRateManager, source::Currency, target::Currency, date::CalendarTime, forbidden::Array{Integer, 1})
86+
function smartLookup(ERM::ExchangeRateManager, source::Currency, target::Currency, date::CalendarTime, forbidden::Array{Int, 1})
8487
fetchedRate=fetch(ERM, source, target, date)
8588

8689
# direct exchange rates are preferred.
@@ -94,20 +97,20 @@ function smartLookup(ERM::ExchangeRateManager, source::Currency, target::Currenc
9497

9598
for key in keys(ERM.data)
9699
# we look for exchange-rate data which involve our source currency
97-
if hashes(key, source) && length(ERM.data[key])!=0
100+
if hashes(key, source) && length(ERM.data[key])!=0
98101
# ...whose other currency is not forbidden...
99-
e=ERM.data[key][1]
102+
e=ERM.data[BigInt(key)][1]
100103
other= source==e.rate.source ? e.rate.target : e.rate.source
101-
102104
if !in(other.numeric, forbidden)
103105
# ...and which carries information for the requested date.
104-
head=fetch(ERM, source, other, date)
105-
106+
head=fetch(ERM, source, other, date)
107+
106108
if head!=nothing
107109
# if we can get to the target from here...
108-
try
109-
tail=smartLookup(ERM, other, target, date, forbidden)
110+
try
111+
tail=smartLookup(ERM, other, target, date, forbidden)
110112
# ..we're done.
113+
return chain(head, tail)
111114
catch
112115
# otherwise, we just discard this rate.
113116
end
@@ -121,6 +124,7 @@ function smartLookup(ERM::ExchangeRateManager, source::Currency, target::Currenc
121124
end
122125

123126
function lookup(ERM::ExchangeRateManager, source::Currency, target::Currency, date::CalendarTime, ERType::Symbol=:Derived)
127+
124128
if source==target
125129
return ExchangeRate(source, target, 1.0)
126130
end
@@ -150,14 +154,16 @@ function lookup(ERM::ExchangeRateManager, source::Currency, target::Currency, da
150154
end
151155

152156
else
153-
return smartLookup(EMR, source, target, date, Array(Int,0))
157+
return smartLookup(ERM, source, target, date, Array(Int,0))
154158
end
155159

156160
end
157161

162+
# Singleton design pattern
158163
function getInstance()
159-
if !isdefined(:ERM_Instance)
160-
global ERM_Instance=ExchangeRateManager()
164+
global ERM_Instance
165+
if !isdefined(Currencies, :ERM_Instance)
166+
ERM_Instance=ExchangeRateManager()
161167
end
162168
return ERM_Instance
163169
end

src/currencies/money.jl

+12-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@ type Money
33
currency::Currency
44
end
55

6-
const ConversionType=:NoConversion # or :BaseCurrencyConversion, :AutomatedConversion
6+
ConversionType=:NoConversion # or :BaseCurrencyConversion, :AutomatedConversion
7+
setConversionType(s::Symbol)=(global ConversionType; ConversionType=s)
8+
9+
staticBaseCurrency=nothing
10+
setStaticBaseCurrency(c::Currency)=(global staticBaseCurrency; staticBaseCurrency=c)
11+
resetStaticBaseCurrency()=(global staticBaseCurrency; staticBaseCurrency=nothing)
712

813
# Constructors
914
Money()=Money(0.0, none)
1015
Money(curr::Currency, val::Float64)=Money(val, curr)
1116

1217
# Transformations
13-
rounded(m::Money)=Money(m.rounding(m.value), m.currency)
14-
baseCurrency(m::Money)=m.currency
18+
rounded(m::Money)=Money(m.currency.rounding(m.value), m.currency)
19+
20+
baseCurrency(m::Money)= staticBaseCurrency==nothing ? m.currency : staticBaseCurrency
1521
+(m::Money)=m
1622
-(m::Money)=Money(-m.value, m.currency)
1723

@@ -25,6 +31,9 @@ baseCurrency(m::Money)=m.currency
2531
/(m::Money, x::Float64)=Money(m.currency, m.value/x)
2632
/(x::Float64, m::Money)=m/x
2733

34+
# Copy function
35+
import Base.copy
36+
copy(m::Money)=Money(m.currency, m.value)
2837

2938
# Print function
3039
import Base.show

src/currencies/moneyop.jl

+27-12
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
function convertTo(m::Money, target::Currency)
33
if m.currency!=target
44
rate=lookup(m.currency, target)
5-
m=rounded(exchange(rate, m))
5+
return rounded(exchange(rate, m))
66
end
7+
return m
78
end
89

910
convertToBase(m::Money)=convertTo(m, baseCurrency(m))
@@ -12,24 +13,38 @@ function functionOperator(m1::Money, m2::Money, op::Function)
1213
if m1.currency==m2.currency
1314
return op(m1.value, m2.value)
1415
elseif ConversionType==:BaseCurrencyConversion
15-
tmp1=copy(m1)
16-
convertToBase(tmp1)
17-
tmp2=copy(m2)
18-
convertToBase(tmp2)
19-
return functionOperator(tpm1, tpm2, op)
16+
tmp1=convertToBase(m1)
17+
tmp2=convertToBase(m2)
18+
return functionOperator(tmp1, tmp2, op)
2019
elseif ConversionType==:AutomatedConversion
21-
tmp=copy(m2)
22-
convertTo(tmp, m1.currency)
20+
tmp=convertTo(m2, m1.currency)
2321
return functionOperator(m1, tmp, op)
2422
else
2523
error("currency mismatch and no conversion specified")
2624
end
2725
end
2826

29-
# Operator functions
30-
+(m1::Money, m2::Money)=functionOperator(m1, m2, +)
31-
-(m1::Money, m2::Money)=functionOperator(m1, m2, -)
27+
# Operator functions that return a value
3228
/(m1::Money, m2::Money)=functionOperator(m1, m2, /)
3329
==(m1::Money, m2::Money)=functionOperator(m1, m2, ==)
3430
<(m1::Money, m2::Money)=functionOperator(m1, m2, <)
35-
close(m1::Money, m2::Money)=functionOperator(m1, m2, (x,y)=>abs(x-y)<10.0^(-10))
31+
close(m1::Money, m2::Money)=functionOperator(m1, m2, (x,y)=>abs(x-y)<10.0^(-10))
32+
33+
function functionOperatorMoney(m1::Money, m2::Money, op::Function)
34+
if m1.currency==m2.currency
35+
return Money(m1.currency, op(m1.value, m2.value))
36+
elseif ConversionType==:BaseCurrencyConversion
37+
tmp1=convertToBase(m1)
38+
tmp2=convertToBase(m2)
39+
return functionOperatorMoney(tmp1, tmp2, op)
40+
elseif ConversionType==:AutomatedConversion
41+
tmp=convertTo(m2, m1.currency)
42+
return functionOperatorMoney(m1, tmp, op)
43+
else
44+
error("currency mismatch and no conversion specified")
45+
end
46+
end
47+
48+
# Operator functions that return a Money
49+
+(m1::Money, m2::Money)=functionOperatorMoney(m1, m2, +)
50+
-(m1::Money, m2::Money)=functionOperatorMoney(m1, m2, -)

test/Ito.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
require("Ito/test/calendars.jl")
44
require("Ito/test/statistics.jl")
55
require("Ito/test/integration.jl")
6-
require("Ito/test/term_structure.jl")
6+
require("Ito/test/term_structure.jl")
7+
require("Ito/test/currencies.jl")

test/currencies.jl

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
require("Ito")
2+
using Base.Test
3+
using Ito.Currencies
4+
5+
# Testing money arithmetic without conversions...
6+
7+
EUR=Currencies.EURCurrency()
8+
9+
m1 = 50000.0 * EUR;
10+
m2 = 100000.0 * EUR;
11+
m3 = 500000.0 * EUR;
12+
13+
Currencies.setConversionType(:NoConversion)
14+
15+
calculated = m1*3.0 + 2.5*m2 - m3/5.0
16+
x = m1.value*3.0 + 2.5*m2.value - m3.value/5.0
17+
expected=Currencies.Money(x, EUR)
18+
19+
@test calculated==expected
20+
21+
# Testing money arithmetic with conversion to base currency...
22+
23+
EUR=Currencies.EURCurrency()
24+
GBP=Currencies.GBPCurrency()
25+
USD=Currencies.USDCurrency()
26+
27+
m1 = 50000.0 * GBP
28+
m2 = 100000.0 * EUR
29+
m3 = 500000.0 * USD
30+
31+
Currencies.clear()
32+
33+
eur_usd=Currencies.ExchangeRate(EUR, USD, 1.2042)
34+
eur_gbp=Currencies.ExchangeRate(EUR, GBP, 0.6612)
35+
Currencies.add(eur_usd)
36+
Currencies.add(eur_gbp)
37+
38+
39+
Currencies.setConversionType(:BaseCurrencyConversion)
40+
Currencies.setStaticBaseCurrency(EUR)
41+
42+
43+
calculated=m1*3.0+2.5*m2-m3/5.0
44+
_round=Currencies.baseCurrency(m1).rounding
45+
x=_round(m1.value*3.0/eur_gbp.rate)+2.5*m2.value-_round(m3.value/(5.0*eur_usd.rate))
46+
expected=Currencies.Money(x, EUR)
47+
48+
Currencies.setConversionType(:NoConversion)
49+
50+
@test calculated==expected
51+
52+
# Testing money arithmetic with automated conversion...
53+
54+
EUR=Currencies.EURCurrency()
55+
GBP=Currencies.GBPCurrency()
56+
USD=Currencies.USDCurrency()
57+
58+
m1 = 50000.0 * GBP
59+
m2 = 100000.0 * EUR
60+
m3 = 500000.0 * USD
61+
62+
Currencies.clear()
63+
64+
eur_usd=Currencies.ExchangeRate(EUR, USD, 1.2042)
65+
eur_gbp=Currencies.ExchangeRate(EUR, GBP, 0.6612)
66+
Currencies.add(eur_usd)
67+
Currencies.add(eur_gbp)
68+
69+
Currencies.setConversionType(:AutomatedConversion)
70+
71+
calculated=(m1*3.0+2.5*m2)-m3/5.0
72+
_round=m1.currency.rounding
73+
x=m1.value*3.0+_round(2.5*m2.value*eur_gbp.rate)-_round((m3.value/5.0)*eur_gbp.rate/eur_usd.rate)
74+
expected=Currencies.Money(x, GBP)
75+
76+
Currencies.setConversionType(:NoConversion)
77+
78+
@test calculated==expected

0 commit comments

Comments
 (0)