/*
Copyright (C) 2009 Ueli Hofstetter
This source code is release under the BSD License.
This file is part of JQuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://jquantlib.org/
JQuantLib is free software: you can redistribute it and/or modify it
under the terms of the JQuantLib license. You should have received a
copy of the license along with this program; if not, please email
. The license is also available online at
.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
JQuantLib is based on QuantLib. http://quantlib.org/
When applicable, the original copyright notice follows this notice.
*/
package org.jquantlib.currencies;
import org.jquantlib.QL;
import org.jquantlib.lang.exceptions.LibraryException;
import org.jquantlib.math.Closeness;
/**
* Cash amount in a given currency.
*/
//FIXME: http://bugs.jquantlib.org/view.php?id=474
public class Money implements Cloneable {
//FIXME: These static methods must be "moved" to class Settings
public static ConversionType conversionType;
public static Currency baseCurrency;
// class fields
private/* @Decimal */double value_;
private Currency currency_;
// constructors
public Money() {
QL.validateExperimentalMode();
this.value_ = (0.0);
}
public Money(final Currency currency, /* @Decimal */final double value) {
QL.validateExperimentalMode();
this.value_ = (value);
this.currency_ = (currency);
}
public Money(/* @Decimal */final double value, final Currency currency) {
QL.validateExperimentalMode();
this.value_ = (value);
this.currency_ = (currency.clone());
}
@Override
public Money clone() {
final Money money = new Money();
money.currency_ = currency_.clone();
money.value_ = value_;
return money;
}
// accessors
public Currency currency() {
return currency_;
}
public/* @Decimal */double value() {
return value_;
}
public Money rounded() {
return new Money(currency_.rounding().operator(value_), currency_);
}
// class based operators
// +() //FIXME: this looks like a mistake in c++
public Money positiveValue() {
return new Money(currency_, value_);
}
// -()
public Money negativeValue() {
return new Money(-value_, currency_);
}
// *=
public Money mulAssign(/* Decimal */final double x) {
value_ *= x;
return this;
}
// /=
public Money divAssign(/* Decimal */final double x) {
value_ /= x;
return this;
}
// +
public Money add(final Money money) {
final Money tmp = clone();
tmp.addAssign(money);
return tmp;
}
// -
public Money sub(final Money money) {
final Money tmp = clone();
tmp.subAssign(money);
return tmp;
}
// *
public Money mul(/* @Decimal */final double x) {
final Money tmp = clone();
tmp.mulAssign(x);
return tmp;
}
public Money div(/* @Decimal */final double x) {
final Money tmp = clone();
tmp.value_ /= x;
return tmp;
}
public boolean notEquals(final Money money) {
// eating dogfood
return !(this.equals(money));
}
public boolean greater(final Money money) {
return money.greater(this);
}
public boolean greaterEqual(final Money money) {
return money.greaterEqual(this);
}
// FIXME: suspicious....
public Money operatorMultiply(/* Decimal */final double value, final Currency c) {
return new Money(value, c);
}
public static Money multiple(final Currency c, /* Decimal */final double value) {
return new Money(value, c);
}
public static Money multiple( /* Decimal */final double value, final Currency c) {
return new Money(value, c);
}
public void convertTo(final Currency target) {
if (currency().ne(target)) {
final ExchangeRate rate = ExchangeRateManager.getInstance().lookup(currency(), target);
// FIXME ... evt. Money should be modified in ExchangeRate directly
final Money money = rate.exchange(this).rounded();
this.currency_ = money.currency_;
this.value_ = money.value_;
}
}
//
// Assignment operations
//
// opr method this right result
// ----- ---------- ------- -------- ------
// += addAssign Money Money this
// -= addAssign Money Money this
//
// Operations
//
// opr method this right result
// ----- ---------- ------- -------- ------
// / addAssign Money Money this
// == equals Money Money boolean
public void convertToBase() {
QL.require((!baseCurrency.empty()) , "no base currency set"); // TODO: message
convertTo(baseCurrency);
}
/**
* += Operator
* @param money The money to be added.
* @return This money instance increased by the specified amount.
*/
public Money addAssign(final Money money) {
if (this.currency_.eq(money.currency_)) {
this.value_ += money.value_;
} else if (conversionType == Money.ConversionType.BaseCurrencyConversion) {
this.convertToBase();
final Money tmp = money.clone();
tmp.convertToBase();
// recursive invocation
this.addAssign(tmp);
} else if (conversionType == Money.ConversionType.AutomatedConversion) {
final Money tmp = money.clone();
tmp.convertTo(currency_);
// recursive invocation
this.addAssign(tmp);
} else
throw new LibraryException("currency mismatch and no conversion specified"); // TODO: message
return this;
}
/**
* -= Operator
* @param money The money to be subtracted.
* @return This money instance decreased by the specified amount.
*/
public Money subAssign(final Money money) {
if (currency_.eq(money.currency_)) {
value_ -= money.value_;
} else if (conversionType == Money.ConversionType.BaseCurrencyConversion) {
this.convertToBase();
final Money tmp = money.clone();
tmp.convertToBase();
// recursive ...
this.subAssign(tmp);
} else if (conversionType == Money.ConversionType.AutomatedConversion) {
final Money tmp = money.clone();
tmp.convertTo(currency_);
this.subAssign(tmp);
} else
throw new LibraryException("currency mismatch and no conversion specified"); // TODO: message
return this;
}
/**
* Returns the the value of this instance divided by another instance.
* @note This instance remains unchanged!
* @param money The amount this instance should be divided to.
* @return The amount of this divided by money.
*/
public double div(final Money money) {
if (currency().eq(money.currency()))
return value_ / money.value();
else if (conversionType == Money.ConversionType.BaseCurrencyConversion) {
final Money tmp1 = this.clone();
tmp1.convertToBase();
final Money tmp2 = money.clone();
tmp2.convertToBase();
// recursive
return this.div(tmp2);
} else if (conversionType == Money.ConversionType.AutomatedConversion) {
final Money tmp = money.clone();
tmp.convertTo(money.currency());
// recursive
return this.div(tmp);
} else
throw new LibraryException("currency mismatch and no conversion specified"); // TODO: message
}
/**
* Operator ==
* @param money The instance this instance should be compared to.
* @return Whether this instance is equal to another instance
*/
public boolean equals(final Money money) {
if (currency().eq(money.currency()))
return value() == money.value();
else if (conversionType == Money.ConversionType.BaseCurrencyConversion) {
final Money tmp1 = this.clone();
tmp1.convertToBase();
final Money tmp2 = money.clone();
tmp2.convertToBase();
// recursive...
return tmp1.equals(tmp2);
} else if (conversionType == Money.ConversionType.AutomatedConversion) {
final Money tmp = money.clone();
tmp.convertTo(this.currency());
return this.equals(tmp);
} else
throw new LibraryException("currency mismatch and no conversion specified"); // TODO: message
}
public boolean less(final Money money) {
if (this.currency().eq(money.currency()))
return value() < money.value();
else if (conversionType == Money.ConversionType.BaseCurrencyConversion) {
final Money tmp1 = this.clone();
tmp1.convertToBase();
final Money tmp2 = money;
tmp2.convertToBase();
return tmp1.less(tmp2);
} else if (conversionType == Money.ConversionType.AutomatedConversion) {
final Money tmp = money;
tmp.convertTo(currency());
return this.less(tmp);
} else
throw new LibraryException("currency mismatch and no conversion specified"); // TODO: message
}
public boolean lessEquals(final Money money) {
if (currency().eq(money.currency()))
return value() <= money.value();
else if (conversionType == Money.ConversionType.BaseCurrencyConversion) {
final Money tmp1 = this.clone();
tmp1.convertToBase();
final Money tmp2 = money;
tmp2.convertToBase();
return tmp1.less(tmp2);
} else if (conversionType == Money.ConversionType.AutomatedConversion) {
final Money tmp = money.clone();
;
tmp.convertTo(this.currency());
return this.less(tmp);
} else
throw new LibraryException("currency mismatch and no conversion specified"); // TODO: message
}
public boolean close(final Money money, /* Size */final int n) {
if (currency().eq(money.currency()))
return Closeness.isClose(value(), money.value(), n);
else if (conversionType == Money.ConversionType.BaseCurrencyConversion) {
final Money tmp1 = this.clone();
tmp1.convertToBase();
final Money tmp2 = money.clone();
tmp2.convertToBase();
return tmp1.close(tmp2, n);
} else if (conversionType == Money.ConversionType.AutomatedConversion) {
final Money tmp = money.clone();
tmp.convertTo(this.currency());
return this.close(tmp, n);
} else
throw new LibraryException("currency mismatch and no conversion specified"); // TODO: message
}
public boolean close_enough(final Money money, /* Size */final int n) {
if (currency().eq(money.currency()))
return Closeness.isCloseEnough(value(), money.value(), n);
else if (conversionType == Money.ConversionType.BaseCurrencyConversion) {
final Money tmp1 = this.clone();
tmp1.convertToBase();
final Money tmp2 = money;
tmp2.convertToBase();
return tmp1.close_enough(tmp2, n);
} else if (conversionType == Money.ConversionType.AutomatedConversion) {
final Money tmp = money;
tmp.convertTo(currency());
return this.close_enough(tmp, n);
} else
throw new LibraryException("currency mismatch and no conversion specified"); // TODO: message
}
//
// Overrides Object
//
@Override
public String toString() {
final Currency currency = currency();
return String.format(currency.format(), rounded().value_, currency.code(), currency.symbol() );
}
//
// inner public enums
//
// enums
public enum ConversionType {
/**
* Do not perform conversions
*/
NoConversion,
/**
* Convert both operands to the base currency before converting
*/
BaseCurrencyConversion,
/**
* Return the result in the currency of the first operand
*/
AutomatedConversion
}
}
Ads help maintain this website.