Browse files

Refactor Currency and add initial Money implementation

  • Loading branch information...
1 parent 82a01ab commit f89e5d0ade544bb02e95517cc5cb6e8a55494682 Hunter Morris committed Nov 29, 2011
View
76 IronSmarkets.Tests/CurrencyTests.cs
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 Smarkets Limited
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+// Partially adapted from: http://moneytype.codeplex.com/
+// See LICENSE-MSPL in the root of this project for details.
+
+using System.Globalization;
+
+using Xunit;
+
+using IronSmarkets.Data;
+
+namespace IronSmarkets.Tests
+{
+ public sealed class CurrencyTests
+ {
+ [Fact]
+ public void CurrencyFromCurrentCultureEqualsCurrentCultureCurrency()
+ {
+ var currency1 = Currency.FromIso3LetterCode(
+ new RegionInfo(CultureInfo.CurrentCulture.LCID).ISOCurrencySymbol);
+ var currency2 = Currency.FromCurrentCulture();
+
+ Assert.Equal(currency1.Description, currency2.Description);
+ }
+
+ [Fact]
+ public void CurrencyFromSpecificCultureInfoIsCorrect()
+ {
+ // Use CultureInfo for es-ES
+ var currency = Currency.FromCultureInfo(new CultureInfo(3082));
+
+ // Ensure that we end up with EUR
+ Assert.Equal(978, currency.Description.IsoNumericCode);
+ Assert.Equal("EUR", currency.Description.Iso3LetterCode);
+ }
+
+ [Fact]
+ public void CurrencyFromSpecificIsoCodeIsCorrect()
+ {
+ var currency = Currency.FromIso3LetterCode("EUR");
+
+ Assert.Equal(978, currency.Description.IsoNumericCode);
+ }
+
+ [Fact]
+ public void CurrencyHasValueEquality()
+ {
+ var currency1 = Currency.FromIso3LetterCode("GBP");
+ var currency2 = Currency.FromIso3LetterCode("GBP");
+ object boxedCurrency2 = currency2;
+
+ Assert.True(currency1.Equals(currency2));
+ Assert.True(currency1.Equals(boxedCurrency2));
+ }
+ }
+}
View
2 IronSmarkets.Tests/IronSmarkets.Tests.csproj
@@ -71,7 +71,9 @@
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ClientTests.cs" />
+ <Compile Include="CurrencyTests.cs" />
<Compile Include="DecimalOddsTests.cs" />
+ <Compile Include="MoneyTests.cs" />
<Compile Include="UidTests.cs" />
</ItemGroup>
<ItemGroup>
View
277 IronSmarkets.Tests/MoneyTests.cs
@@ -0,0 +1,277 @@
+// Copyright (c) 2011 Smarkets Limited
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROm, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+// Partially adapted from: http://moneytype.codeplex.com/
+// See LICENSE-MSPL in the root of this project for details.
+
+using System;
+
+using Xunit;
+
+using IronSmarkets.Data;
+
+namespace IronSmarkets.Tests
+{
+ public sealed class MoneyTests
+ {
+ [Fact]
+ public void MoneyHasValueEquality()
+ {
+ var money1 = new Money(101.5m);
+ var money2 = new Money(101.5m);
+ Assert.Equal(money1, money2);
+ }
+
+ [Fact]
+ public void MoneyWholeAmountAdditionIsCorrect()
+ {
+ var money1 = new Money(101m);
+ var money2 = new Money(99m);
+
+ Assert.Equal(new Money(200m), money1 + money2);
+ }
+
+ [Fact]
+ public void MoneyFractionalAmountAdditionIsCorrect()
+ {
+ var money1 = new Money(100.00m);
+ var money2 = new Money(0.01m);
+
+ Assert.Equal(new Money(100.01m), money1 + money2);
+
+ }
+
+ [Fact]
+ public void MoneyFractionalAmountWithOverflowAdditionIsCorrect()
+ {
+ var money1 = new Money(100.999M);
+ var money2 = new Money(0.9M);
+
+ Assert.Equal(new Money(101.899m), money1 + money2);
+ }
+
+ [Fact]
+ public void MoneyNegativeAmountAdditionIsCorrect()
+ {
+ var money1 = new Money(100.999M);
+ var money2 = new Money(-0.9M);
+
+ Assert.Equal(new Money(100.099m), money1 + money2);
+ }
+
+ [Fact]
+ public void MoneyNegativeAmountWithOverflowAdditionIsCorrect()
+ {
+ var money1 = new Money(-100.999M);
+ var money2 = new Money(-0.9M);
+
+ Assert.Equal(new Money(-101.899m), money1 + money2);
+ }
+
+ [Fact]
+ public void MoneyWholeAmountSubtractionIsCorrect()
+ {
+ var money1 = new Money(101m);
+ var money2 = new Money(99m);
+
+ Assert.Equal(new Money(2), money1 - money2);
+ }
+
+ [Fact]
+ public void MoneyFractionalAmountSubtractionIsCorrect()
+ {
+ var money1 = new Money(100.00m);
+ var money2 = new Money(0.01m);
+
+ Assert.Equal(new Money(99.99m), money1 - money2);
+ }
+
+ [Fact]
+ public void MoneyFractionalAmountWithOverflowSubtractionIsCorrect()
+ {
+ var money1 = new Money(100.5m);
+ var money2 = new Money(0.9m);
+
+ Assert.Equal(new Money(99.6m), money1 - money2);
+ }
+
+ [Fact]
+ public void MoneyNegativeAmountSubtractionIsCorrect()
+ {
+ var money1 = new Money(100.999m);
+ var money2 = -new Money(0.9m);
+
+ Assert.Equal(new Money(101.899m), money1 - money2);
+ }
+
+ [Fact]
+ public void MoneyNegativeAmountWithOverflowSubtractionIsCorrect()
+ {
+ var money1 = -new Money(100.999m);
+ var money2 = -new Money(0.9m);
+
+ Assert.Equal(new Money(-100.099m), money1 - money2);
+ }
+
+ [Fact]
+ public void MoneyScalarWholeMultiplicationIsCorrect()
+ {
+ var money = new Money(100.125m);
+
+ Assert.Equal(500.625m, money * 5);
+ }
+
+ [Fact]
+ public void MoneyScalarFractionalMultiplicationIsCorrect()
+ {
+ var money = new Money(100.125m);
+
+ Assert.Equal(50.0625m, money * 0.5m);
+ }
+
+ [Fact]
+ public void MoneyScalarNegativeWholeMultiplicationIsCorrect()
+ {
+ var money = -new Money(100.125m);
+
+ Assert.Equal(-500.625m, money * 5);
+ }
+
+ [Fact]
+ public void MoneyScalarNegativeFractionalMultiplicationIsCorrect()
+ {
+ var money = -new Money(100.125m);
+
+ Assert.Equal(-50.0625m, money * 0.5m);
+ }
+
+ [Fact]
+ public void MoneyScalarWholeDivisionIsCorrect()
+ {
+ var money = new Money(100.125m);
+
+ Assert.Equal(50.0625m, money / 2m);
+ }
+
+ [Fact]
+ public void MoneyScalarFractionalDivisionIsCorrect()
+ {
+ var money = new Money(100.125m);
+
+ Assert.Equal(200.25m, money / 0.5m);
+ }
+
+ [Fact]
+ public void MoneyScalarNegativeWholeDivisionIsCorrect()
+ {
+ var money = -new Money(100.125m);
+
+ Assert.Equal(-50.0625m, money / 2m);
+ }
+
+ [Fact]
+ public void MoneyScalarNegativeFractionalDivisionIsCorrect()
+ {
+ var money = -new Money(100.125m);
+
+ Assert.Equal(-200.25m, money / 0.5m);
+ }
+
+ [Fact]
+ public void MoneyEqualOperatorIsCorrect()
+ {
+ var money1 = new Money(100.125m);
+ var money2 = new Money(100.125m);
+
+ Assert.True(money1 == money2);
+ }
+
+ [Fact]
+ public void MoneyNotEqualOperatorIsCorrect()
+ {
+ var money1 = new Money(100.125m);
+ var money2 = new Money(100.125m);
+
+ Assert.False(money1 != money2);
+ }
+
+ [Fact]
+ public void MoneyGreaterThanEqualOperatorIsCorrect()
+ {
+ var money1 = new Money(100.125m);
+ var money2 = new Money(100.125m);
+
+ Assert.True(money1 >= money2);
+ }
+
+ [Fact]
+ public void MoneyLessThanEqualOperatorIsCorrect()
+ {
+ var money1 = new Money(100.125m);
+ var money2 = new Money(100.125m);
+
+ Assert.True(money1 <= money2);
+ }
+
+ [Fact]
+ public void MoneyGreaterThanOperatorIsCorrect()
+ {
+ var money1 = new Money(100.125m);
+ var money2 = new Money(100.125m);
+
+ Assert.False(money1 > money2);
+ }
+
+ [Fact]
+ public void MoneyLessThanOperatorIsCorrect()
+ {
+ var money1 = new Money(100.125m);
+ var money2 = new Money(100.125m);
+
+ Assert.False(money1 < money2);
+ }
+
+ [Fact]
+ public void MoneyPrintsCorrectly()
+ {
+ var money = new Money(100.125m, Currency.Gbp);
+ var formattedMoney = money.ToString();
+ Assert.Equal("£100.13", formattedMoney);
+ }
+
+ [Fact]
+ public void MoneyOperationsInvolvingDifferentCurrencyAllFail()
+ {
+ var money1 = new Money(101.5m, Currency.Gbp);
+ var money2 = new Money(98.5m, Currency.Eur);
+
+ Assert.Throws<InvalidOperationException>(() => money1 + money2);
+ Assert.Throws<InvalidOperationException>(() => money1 - money2);
+ Assert.Throws<InvalidOperationException>(() => money1 > money2);
+ Assert.Throws<InvalidOperationException>(() => money1 < money2);
+ Assert.Throws<InvalidOperationException>(() => money1 >= money2);
+ Assert.Throws<InvalidOperationException>(() => money1 <= money2);
+ Assert.DoesNotThrow(() => { Assert.False(money1 == money2); });
+ Assert.DoesNotThrow(() => { Assert.True(money1 != money2); });
+ }
+ }
+}
View
304 IronSmarkets/Data/Currency.cs
@@ -19,77 +19,251 @@
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
+//
+// Partially adapted from: http://moneytype.codeplex.com/
+// See LICENSE-MSPL in the root of this project for details.
using System;
using System.Collections.Generic;
+using System.Globalization;
namespace IronSmarkets.Data
{
- public struct Currency : IEquatable<Currency>
+ internal struct CurrencyDescription : IEquatable<CurrencyDescription>
+ {
+ public readonly string Name;
+ public readonly string Iso3LetterCode;
+ public readonly int IsoNumericCode;
+ public readonly string Symbol;
+
+ internal CurrencyDescription(
+ string name, string iso3LetterCode,
+ int isoNumberCode, string symbol)
+ {
+ Name = name;
+ Iso3LetterCode = iso3LetterCode;
+ IsoNumericCode = isoNumberCode;
+ Symbol = symbol;
+ }
+
+ public override int GetHashCode()
+ {
+ return Name.GetHashCode()
+ ^ Iso3LetterCode.GetHashCode()
+ ^ IsoNumericCode.GetHashCode()
+ ^ Symbol.GetHashCode();
+ }
+
+ public override bool Equals(object right)
+ {
+ if (ReferenceEquals(right, null))
+ return false;
+
+ if (GetType() != right.GetType())
+ return false;
+
+ return Equals((CurrencyDescription)right);
+ }
+
+ public bool Equals(CurrencyDescription other)
+ {
+ return IsoNumericCode == other.IsoNumericCode
+ && Name == other.Name
+ && Iso3LetterCode == other.Iso3LetterCode
+ && Symbol == other.Symbol;
+ }
+
+ public static bool operator==(
+ CurrencyDescription left, CurrencyDescription right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator!=(
+ CurrencyDescription left, CurrencyDescription right)
+ {
+ return !left.Equals(right);
+ }
+ }
+
+ public struct Currency : IEquatable<Currency>, IFormatProvider
{
- private static readonly IDictionary<Proto.Seto.Currency, Currency> Currencies =
- new Dictionary<Proto.Seto.Currency, Currency>
- {
- { Proto.Seto.Currency.CURRENCYGBP, new Currency("Pounds Sterling", "GBP") },
- { Proto.Seto.Currency.CURRENCYEUR, new Currency("Euros", "EUR") }
- };
-
- private readonly string _name;
- private readonly string _iso4217;
-
- public string Name { get { return _name; } }
- public string Iso4217 { get { return _iso4217; } }
-
- private Currency(string name, string iso4217)
- {
- _name = name;
- _iso4217 = iso4217;
- }
-
- public override int GetHashCode()
- {
- return _name.GetHashCode() ^ _iso4217.GetHashCode();
- }
-
- public override bool Equals(object right)
- {
- if (ReferenceEquals(right, null))
- return false;
-
- if (GetType() != right.GetType())
- return false;
-
- return Equals((Currency)right);
- }
-
- public bool Equals(Currency other)
- {
- return _name == other._name && _iso4217 == other._iso4217;
- }
-
- public static bool operator==(Currency left, Currency right)
- {
- return left.Equals(right);
- }
-
- public static bool operator!=(Currency left, Currency right)
- {
- return !left.Equals(right);
- }
-
- public override string ToString()
- {
- return _iso4217;
- }
-
- public static Currency FromSeto(Proto.Seto.Currency currency)
- {
- Currency cur;
- if (!Currencies.TryGetValue(currency, out cur))
- {
- throw new ArgumentException("Invalid currency.");
- }
- return cur;
- }
+ public static readonly Currency Gbp;
+ public static readonly Currency Eur;
+
+ private static readonly IDictionary<int, CurrencyDescription> CurrencyDescriptions =
+ new Dictionary<int, CurrencyDescription>();
+ private static readonly IDictionary<int, int> CodesByCultureId =
+ new Dictionary<int, int>();
+ private static readonly IDictionary<int, int> CultureIdsByCode =
+ new Dictionary<int, int>();
+ private static readonly IDictionary<string, int> CodesByIso3Letters =
+ new Dictionary<string, int>();
+
+ private static readonly IDictionary<Proto.Seto.Currency, Currency> Currencies;
+
+ private readonly int _code;
+
+ public int Code { get { return _code; } }
+
+ internal CurrencyDescription Description
+ {
+ get
+ {
+ CurrencyDescription description;
+ if (!CurrencyDescriptions.TryGetValue(_code, out description))
+ throw new InvalidOperationException(
+ String.Format("Unknown currency code: {0}", _code));
+ return description;
+ }
+ }
+
+ private static TValue GetValueOrNull<TKey, TValue>(TKey key, IDictionary<TKey, TValue> table) where TValue : class
+ {
+ TValue value;
+ return !table.TryGetValue(key, out value) ? null : value;
+ }
+
+ static Currency()
+ {
+ var cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
+
+ var cultureIdLookup = new Dictionary<string, List<int>>();
+ var symbolLookup = new Dictionary<string, string>();
+
+ foreach (var culture in cultures)
+ {
+ var lcid = culture.LCID;
+ var regionInfo = new RegionInfo(lcid);
+ var isoSymbol = regionInfo.ISOCurrencySymbol;
+
+ if (!cultureIdLookup.ContainsKey(isoSymbol))
+ cultureIdLookup[isoSymbol] = new List<int>();
+
+ cultureIdLookup[isoSymbol].Add(lcid);
+ symbolLookup[isoSymbol] = regionInfo.CurrencySymbol;
+ }
+
+ CurrencyDescriptions[826] = new CurrencyDescription(
+ "Pound Sterling", "GBP", 826, GetValueOrNull("GBP", symbolLookup));
+ CurrencyDescriptions[978] = new CurrencyDescription(
+ "Euro", "EUR", 978, GetValueOrNull("EUR", symbolLookup));
+
+ foreach (var currency in CurrencyDescriptions.Values)
+ {
+ var iso3LetterCode = currency.Iso3LetterCode;
+ List<int> lcids;
+
+ if (cultureIdLookup.TryGetValue(iso3LetterCode, out lcids))
+ {
+ foreach (var lcid in lcids)
+ {
+ CodesByCultureId[lcid] = currency.IsoNumericCode;
+ CultureIdsByCode[currency.IsoNumericCode] = lcid;
+ }
+ }
+
+ CodesByIso3Letters[iso3LetterCode] = currency.IsoNumericCode;
+ }
+
+ Gbp = new Currency(826);
+ Eur = new Currency(978);
+
+ Currencies = new Dictionary<Proto.Seto.Currency, Currency>
+ {
+ { Proto.Seto.Currency.CURRENCYGBP, Gbp },
+ { Proto.Seto.Currency.CURRENCYEUR, Eur }
+ };
+ }
+
+ private Currency(int code)
+ {
+ if (!CurrencyDescriptions.ContainsKey(code))
+ throw new ArgumentOutOfRangeException(
+ "code", code,
+ "This value is not a valid ISO 4217 currency code.");
+ _code = code;
+ }
+
+ internal static Currency FromIso3LetterCode(string iso3LetterCode)
+ {
+ int code;
+ if (!CodesByIso3Letters.TryGetValue(iso3LetterCode, out code))
+ throw new ArgumentOutOfRangeException(
+ "iso3LetterCode", iso3LetterCode,
+ "Invalid ISO 3 letter code");
+ return new Currency(code);
+ }
+
+ public static Currency FromCurrentCulture()
+ {
+ return FromCultureInfo(CultureInfo.CurrentCulture);
+ }
+
+ public static Currency FromCultureInfo(CultureInfo cultureInfo)
+ {
+ int code;
+ if (!CodesByCultureId.TryGetValue(cultureInfo.LCID, out code))
+ throw new ArgumentOutOfRangeException(
+ "cultureInfo", cultureInfo,
+ "Invalid CultureInfo value");
+ return new Currency(code);
+ }
+
+ public override int GetHashCode()
+ {
+ return _code.GetHashCode();
+ }
+
+ public override bool Equals(object right)
+ {
+ if (ReferenceEquals(right, null))
+ return false;
+
+ if (GetType() != right.GetType())
+ return false;
+
+ return Equals((Currency)right);
+ }
+
+ public bool Equals(Currency other)
+ {
+ return _code == other._code;
+ }
+
+ public static bool operator==(Currency left, Currency right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator!=(Currency left, Currency right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override string ToString()
+ {
+ var d = Description;
+ return String.Format(
+ "{0} ({1})",
+ d.Name,
+ d.Iso3LetterCode);
+ }
+
+ public static Currency FromSeto(Proto.Seto.Currency currency)
+ {
+ Currency cur;
+ if (!Currencies.TryGetValue(currency, out cur))
+ throw new ArgumentException("Invalid currency.");
+ return cur;
+ }
+
+ public object GetFormat(Type formatType)
+ {
+ int lcid;
+ if (!CultureIdsByCode.TryGetValue(_code, out lcid))
+ return null;
+ return new CultureInfo(lcid).NumberFormat;
+ }
}
}
View
253 IronSmarkets/Data/Money.cs
@@ -0,0 +1,253 @@
+// Copyright (c) 2011 Smarkets Limited
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+// Partially adapted from: http://moneytype.codeplex.com/
+// See LICENSE-MSPL in the root of this project for details.
+
+using System;
+
+namespace IronSmarkets.Data
+{
+ public struct Money : IEquatable<Money>, IComparable<Money>, IFormattable, IConvertible
+ {
+ private readonly Currency _currency;
+ private readonly decimal _units;
+
+ public Currency Currency { get { return _currency; } }
+ public decimal Units { get { return _units; } }
+
+ public Money(decimal units) : this(units, Currency.FromCurrentCulture())
+ {
+ }
+
+ public Money(decimal units, Currency currency)
+ {
+ AssertValidValue(units);
+ _units = units;
+ _currency = currency;
+ }
+
+ public int CompareTo(Money other)
+ {
+ AssertSameCurrencies(this, other);
+ return _units.CompareTo(other._units);
+ }
+
+ public override string ToString()
+ {
+ return ToString("C");
+ }
+
+ private string ToString(string format)
+ {
+ return ToString(format, _currency);
+ }
+
+ public string ToString(string format, IFormatProvider formatProvider)
+ {
+ return _units.ToString(format, formatProvider);
+ }
+
+ public static Money operator -(Money value)
+ {
+ return new Money(-value._units, value._currency);
+ }
+
+ public static Money operator +(Money left, Money right)
+ {
+ AssertSameCurrencies(left, right);
+ return new Money(left._units + right._units, left._currency);
+ }
+
+ public static Money operator -(Money left, Money right)
+ {
+ AssertSameCurrencies(left, right);
+ return new Money(left._units - right._units, left._currency);
+ }
+
+ public static decimal operator *(Money left, decimal right)
+ {
+ return left._units * right;
+ }
+
+ public static decimal operator /(Money left, decimal right)
+ {
+ return left._units / right;
+ }
+
+ public static bool operator >(Money left, Money right)
+ {
+ return left.CompareTo(right) > 0;
+ }
+
+ public static bool operator <(Money left, Money right)
+ {
+ return left.CompareTo(right) < 0;
+ }
+
+ public static bool operator >=(Money left, Money right)
+ {
+ return left.CompareTo(right) >= 0;
+ }
+
+ public static bool operator <=(Money left, Money right)
+ {
+ return left.CompareTo(right) <= 0;
+ }
+
+ public override int GetHashCode()
+ {
+ return _units.GetHashCode()
+ ^ _currency.GetHashCode();
+ }
+
+ public override bool Equals(object right)
+ {
+ if (ReferenceEquals(right, null))
+ return false;
+
+ if (GetType() != right.GetType())
+ return false;
+
+ return Equals((Money)right);
+ }
+
+ public bool Equals(Money other)
+ {
+ return _units == other._units
+ && _currency == other._currency;
+ }
+
+ public static bool operator==(Money left, Money right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator!=(Money left, Money right)
+ {
+ return !left.Equals(right);
+ }
+
+ private static void AssertValidValue(decimal value)
+ {
+ if (value < Int64.MinValue || value > Int64.MaxValue)
+ throw new ArgumentOutOfRangeException(
+ "value", value,
+ string.Format(
+ "Money value must be between {0} and {1}",
+ Int64.MinValue, Int64.MaxValue));
+ }
+
+ private static void AssertSameCurrencies(Money left, Money right)
+ {
+ if (left._currency != right._currency)
+ throw new InvalidOperationException(
+ "Only Money values with the same currency " +
+ "may be compared or otherwise operated on.");
+ }
+
+ public TypeCode GetTypeCode()
+ {
+ return TypeCode.Object;
+ }
+
+ public bool ToBoolean(IFormatProvider provider)
+ {
+ return _units != 0;
+ }
+
+ public char ToChar(IFormatProvider provider)
+ {
+ throw new NotSupportedException();
+ }
+
+ public sbyte ToSByte(IFormatProvider provider)
+ {
+ return (sbyte)_units;
+ }
+
+ public byte ToByte(IFormatProvider provider)
+ {
+ return (byte)_units;
+ }
+
+ public short ToInt16(IFormatProvider provider)
+ {
+ return (short)_units;
+ }
+
+ public ushort ToUInt16(IFormatProvider provider)
+ {
+ return (ushort)_units;
+ }
+
+ public int ToInt32(IFormatProvider provider)
+ {
+ return (int)_units;
+ }
+
+ public uint ToUInt32(IFormatProvider provider)
+ {
+ return (uint)_units;
+ }
+
+ public long ToInt64(IFormatProvider provider)
+ {
+ return (long)_units;
+ }
+
+ public ulong ToUInt64(IFormatProvider provider)
+ {
+ return (ulong)_units;
+ }
+
+ public float ToSingle(IFormatProvider provider)
+ {
+ return (float)_units;
+ }
+
+ public double ToDouble(IFormatProvider provider)
+ {
+ return (double)_units;
+ }
+
+ public decimal ToDecimal(IFormatProvider provider)
+ {
+ return _units;
+ }
+
+ public DateTime ToDateTime(IFormatProvider provider)
+ {
+ throw new NotSupportedException();
+ }
+
+ string IConvertible.ToString(IFormatProvider provider)
+ {
+ return ToString("C", provider);
+ }
+
+ public object ToType(Type conversionType, IFormatProvider provider)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
View
1 IronSmarkets/IronSmarkets.csproj
@@ -94,6 +94,7 @@
<Compile Include="Data\MarketInfo.cs" />
<Compile Include="Data\MarketMap.cs" />
<Compile Include="Data\MarketQuotes.cs" />
+ <Compile Include="Data\Money.cs" />
<Compile Include="Data\Order.cs" />
<Compile Include="Data\OrderMap.cs" />
<Compile Include="Data\OrderState.cs" />
View
70 LICENSE-MSPL
@@ -0,0 +1,70 @@
+Code in the following files:
+
+IronSmarkets/Data/Money.cs
+IronSmarkets/Data/Currency.cs
+IronSmarkets.Tests/CurrencyTests.cs
+IronSmarkets.Tests/MoneyTests.cs
+
+is based on code from http://moneytype.codeplex.com/ available under
+the following license:
+
+Microsoft Public License (MS-PL)
+
+This license governs use of the accompanying software. If you use the
+software, you accept this license. If you do not accept the license,
+do not use the software.
+
+1. Definitions
+
+The terms "reproduce," "reproduction," "derivative works," and
+"distribution" have the same meaning here as under U.S. copyright law.
+
+A "contribution" is the original software, or any additions or changes
+to the software.
+
+A "contributor" is any person that distributes its contribution under
+this license. "Licensed patents" are a contributor's patent claims
+that read directly on its contribution.
+
+2. Grant of Rights
+
+(A) Copyright Grant- Subject to the terms of this license, including
+ the license conditions and limitations in section 3, each
+ contributor grants you a non-exclusive, worldwide, royalty-free
+ copyright license to reproduce its contribution, prepare
+ derivative works of its contribution, and distribute its
+ contribution or any derivative works that you create.
+
+(B) Patent Grant- Subject to the terms of this license, including the
+ license conditions and limitations in section 3, each contributor
+ grants you a non-exclusive, worldwide, royalty-free license under
+ its licensed patents to make, have made, use, sell, offer for
+ sale, import, and/or otherwise dispose of its contribution in the
+ software or derivative works of the contribution in the software.
+
+3. Conditions and Limitations
+
+(A) No Trademark License- This license does not grant you rights to
+ use any contributors' name, logo, or trademarks.
+
+(B) If you bring a patent claim against any contributor over patents
+ that you claim are infringed by the software, your patent license
+ from such contributor to the software ends automatically.
+
+(C) If you distribute any portion of the software, you must retain all
+ copyright, patent, trademark, and attribution notices that are
+ present in the software.
+
+(D) If you distribute any portion of the software in source code form,
+ you may do so only under this license by including a complete copy
+ of this license with your distribution. If you distribute any
+ portion of the software in compiled or object code form, you may
+ only do so under a license that complies with this license.
+
+(E) The software is licensed "as-is." You bear the risk of using
+ it. The contributors give no express warranties, guarantees or
+ conditions. You may have additional consumer rights under your
+ local laws which this license cannot change. To the extent
+ permitted under your local laws, the contributors exclude the
+ implied warranties of merchantability, fitness for a particular
+ purpose and non-infringement.

0 comments on commit f89e5d0

Please sign in to comment.