Beispiel #1
0
        public Account(Guid accountId, AccountType accountType, Security security, Account parentAccount, string name, int? smallestFraction)
        {
            if (accountId == Guid.Empty)
            {
                throw new ArgumentOutOfRangeException("accountId");
            }

            if (!Enum.GetValues(typeof(AccountType)).Cast<AccountType>().Contains(accountType))
            {
                throw new ArgumentOutOfRangeException("accountType");
            }

            if (security == null && smallestFraction.HasValue)
            {
                throw new ArgumentNullException("security");
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            if (security != null && !smallestFraction.HasValue)
            {
                throw new ArgumentNullException("smallestFraction");
            }

            if (smallestFraction <= 0)
            {
                throw new ArgumentOutOfRangeException("smallestFraction");
            }

            if (security != null)
            {
                if (security.FractionTraded % smallestFraction != 0)
                {
                    throw new InvalidOperationException("An account's smallest fraction must represent a whole number multiple of the units used by its security");
                }
            }

            var parent = parentAccount;
            while (parent != null)
            {
                if (parent.AccountId == accountId)
                {
                    throw new InvalidOperationException("An account may not share an its Account Id with any of its ancestors.");
                }

                parent = parent.ParentAccount;
            }

            this.accountId = accountId;
            this.accountType = accountType;
            this.security = security;
            this.parentAccount = parentAccount;
            this.name = name;
            this.smallestFraction = smallestFraction;
        }
Beispiel #2
0
        public PriceQuote(Guid priceQuoteId, DateTime dateTime, Security security, long quantity, Security currency, long price, string source)
        {
            if (priceQuoteId == Guid.Empty)
            {
                throw new ArgumentOutOfRangeException("priceQuoteId");
            }

            if (dateTime.Kind != DateTimeKind.Utc)
            {
                throw new ArgumentOutOfRangeException("dateTime");
            }

            if (security == null)
            {
                throw new ArgumentNullException("security");
            }

            if (currency == null)
            {
                throw new ArgumentNullException("currency");
            }

            if (string.IsNullOrEmpty(source))
            {
                throw new ArgumentNullException("source");
            }

            if (quantity <= 0)
            {
                throw new ArgumentOutOfRangeException("quantity");
            }

            if (currency.SecurityType != SecurityType.Currency)
            {
                throw new InvalidOperationException("Could not create a price quote, because the currency parameter was not a valid currency.");
            }

            if (currency == security)
            {
                throw new InvalidOperationException("Could not create a price quote, because the security and currency were the same.");
            }

            if (price <= 0)
            {
                throw new ArgumentOutOfRangeException("price");
            }

            this.PriceQuoteId = priceQuoteId;
            this.DateTime = dateTime;
            this.Security = security;
            this.Quantity = quantity;
            this.Currency = currency;
            this.Price = price;
            this.Source = source;
        }
Beispiel #3
0
        public Balance(Security security, long amount, bool isExact)
        {
            if (security == null)
            {
                throw new ArgumentNullException("security");
            }

            this.security = security;
            this.amount = amount;
            this.isExact = isExact;
        }
Beispiel #4
0
        public SecurityData(Security security)
        {
            if (security == null)
            {
                throw new ArgumentNullException("security");
            }

            this.SecurityId = security.SecurityId;
            this.SecurityType = security.SecurityType;
            this.Symbol = security.Symbol;
            this.Name = security.Name;
            this.Format = security.Format;
            this.FractionTraded = security.FractionTraded;
        }
Beispiel #5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SharpBooks.Transaction"/> class.
        /// </summary>
        /// <param name="transactionId">The unique identifier of the transaction.</param>
        /// <param name="baseSecurity">The security in which the values of the transaction are expressed.</param>
        public Transaction(Guid transactionId, Security baseSecurity)
        {
            if (baseSecurity == null)
            {
                throw new ArgumentNullException("baseSecurity");
            }

            if (transactionId == Guid.Empty)
            {
                throw new ArgumentOutOfRangeException("transactionId");
            }

            this.BaseSecurity = baseSecurity;
            this.TransactionId = transactionId;
            this.Date = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
        }
Beispiel #6
0
        /// <summary>
        /// Adds an amount to the current <see cref="CompositeBalance"/> and returns the new composite balance.
        /// </summary>
        /// <param name="security">The security of the amount.</param>
        /// <param name="amount">The amount to add.</param>
        /// <returns>The new composite balance.</returns>
        /// <remarks>
        /// If any of the component balances can be reused, they are.
        /// </remarks>
        public CompositeBalance CombineWith(Security security, long amount, bool isExact)
        {
            // If the new amount will not change the balances in any way, return the original balance.
            if (amount == 0 && isExact)
            {
                return this;
            }

            // Create a new storage location, since we will be changing balances.
            var newBalances = new List<Balance>(this.balances.Count + 1);

            // Go through the list of current balances.
            bool combined = false;
            foreach (var bal in this.balances)
            {
                // Reuse the existing balance, if possible.
                if (bal.Security != security)
                {
                    newBalances.Add(bal);
                }
                else
                {
                    combined = true;

                    // If it must be combined, calculate the new amount and propagate the inexact flag.
                    var newAmount = bal.Amount + amount;
                    var newIsExact = bal.IsExact && isExact;

                    // If the result is either not exact, or not zero, add it.
                    if (newAmount != 0 || !newIsExact)
                    {
                        newBalances.Add(new Balance(security, newAmount, newIsExact));
                    }
                }
            }

            // If it was not combined with an existing balance, create a new one for it.
            if (!combined)
            {
                newBalances.Add(new Balance(security, amount, isExact));
            }

            return new CompositeBalance(newBalances.AsReadOnly());
        }
Beispiel #7
0
        /// <summary>
        /// Removes a security from the <see cref="Book"/>.
        /// </summary>
        /// <param name="security">The security to remove.</param>
        public void RemoveSecurity(Security security)
        {
            lock (this.lockMutex)
            {
                if (security == null)
                {
                    throw new ArgumentNullException("security");
                }

                if (!this.securities.Contains(security))
                {
                    throw new InvalidOperationException("Could not remove the security from the book, because the security is not a member of the book.");
                }

                var dependantAccounts = from a in this.accounts
                                        where a.Security == security
                                        select a;

                if (dependantAccounts.Any())
                {
                    throw new InvalidOperationException("Could not remove the security from the book, because at least one account depends on it.");
                }

                var dependantSplits = from t in this.transactions.Keys
                                      from s in t.Splits
                                      where s.Security == security
                                      select s;

                if (dependantSplits.Any())
                {
                    throw new InvalidOperationException("Could not remove the security from the book, because at least one transaction depends on it.");
                }

                var dependantPriceQuotes = from q in this.priceQuotes
                                           where q.Security == security || q.Currency == security
                                           select q;

                if (dependantPriceQuotes.Any())
                {
                    throw new InvalidOperationException("Could not remove the security from the book, because at least one price quote depends on it.");
                }

                this.securities.Remove(security);
                this.UpdateSaveTracks(st => st.RemoveSecurity(security.SecurityId));
            }

            this.SecurityRemoved.SafeInvoke(this, new SecurityRemovedEventArgs(security));
        }
Beispiel #8
0
        /// <summary>
        /// Adds a security to the <see cref="Book"/>.
        /// </summary>
        /// <param name="security">The security to add.</param>
        public void AddSecurity(Security security)
        {
            lock (this.lockMutex)
            {
                if (security == null)
                {
                    throw new ArgumentNullException("security");
                }

                if (this.securities.Contains(security))
                {
                    throw new InvalidOperationException("Could not add the security to the book, because the security already belongs to the book.");
                }

                var duplicateIds = from s in this.securities
                                   where s.SecurityId == security.SecurityId
                                   select s;

                if (duplicateIds.Any())
                {
                    throw new InvalidOperationException("Could not add the security to the book, because another security has already been added with the same Security Id.");
                }

                var duplicateData = from s in this.securities
                                    where s.SecurityType == security.SecurityType
                                    where string.Equals(s.Symbol, security.Symbol, StringComparison.OrdinalIgnoreCase)
                                    select s;

                if (duplicateData.Any())
                {
                    throw new InvalidOperationException(
                        "Could not add the security to the book, because another security has already been added with the same Security Type and Symbol.");
                }

                this.securities.Add(security);
                this.UpdateSaveTracks(st => st.AddSecurity(new SecurityData(security)));
            }

            this.SecurityAdded.SafeInvoke(this, new SecurityAddedEventArgs(security));
        }
        /// <summary>
        /// Retrieves a price quote from Google™ Calculator.
        /// </summary>
        /// <param name="security">The security for which to get the quote.</param>
        /// <param name="currency">The currency in which to express the quote.</param>
        /// <returns>The requested price quote.</returns>
        public PriceQuote GetPriceQuote(Security security, Security currency)
        {
            if (security == null)
            {
                throw new ArgumentNullException("security");
            }

            if (currency == null)
            {
                throw new ArgumentNullException("currency");
            }

            if (currency.SecurityType != SecurityType.Currency)
            {
                throw new ArgumentException("The argument must be a Security with a SecurityType of Currency", "currency");
            }

            if (security.SecurityType != SecurityType.Currency)
            {
                throw BuildError(security.Symbol, "Only currencies are supported.");
            }

            var client = new WebClient();
            string data;

            try
            {
                data = client.DownloadString(string.Format(UrlFormat, security.Symbol, currency.Symbol));
            }
            catch (WebException ex)
            {
                throw BuildError(security.Symbol + currency.Symbol, "Check the inner exception for details.", ex);
            }

            DateTimeOffset date;
            decimal price;
            try
            {
                var result = Parse(data).Last();
                date = result.Item1;
                price = result.Item2;
            }
            catch (InvalidOperationException ex)
            {
                throw BuildError(security.Symbol + currency.Symbol, "Check the inner exception for details.", ex);
            }

            checked
            {
                var quantity = (long)security.FractionTraded;
                price *= currency.FractionTraded;

                var longPrice = (long)Math.Floor(price);

                while (longPrice != price)
                {
                    price *= 10;
                    quantity *= 10;
                    longPrice = (long)Math.Floor(price);
                }

                var gcd = FinancialMath.GCD(longPrice, quantity / security.FractionTraded);

                quantity /= gcd;
                longPrice /= gcd;

                return new PriceQuote(Guid.NewGuid(), date.UtcDateTime, security, quantity, currency, longPrice, "Google™ Calculator");
            }
        }
Beispiel #10
0
 public SecurityOption(Security security)
 {
     this.security = security;
 }
Beispiel #11
0
        public void SetSecurity(Security security, TransactionLock transactionLock)
        {
            this.Transaction.EnterCriticalSection();

            try
            {
                this.Transaction.ValidateLock(transactionLock);

                this.Security = security;
            }
            finally
            {
                this.Transaction.ExitCriticalSection();
            }
        }
        /// <summary>
        /// Retrieves a price quote from Yahoo!® Finance.
        /// </summary>
        /// <param name="security">The security for which to get the quote.</param>
        /// <param name="currency">The currency in which to express the quote.</param>
        /// <returns>The requested price quote.</returns>
        public PriceQuote GetPriceQuote(Security security, Security currency)
        {
            if (security == null)
            {
                throw new ArgumentNullException("security");
            }

            if (currency == null)
            {
                throw new ArgumentNullException("currency");
            }

            if (currency.SecurityType != SecurityType.Currency)
            {
                throw new ArgumentException("The argument must be a Security with a SecurityType of Currency", "currency");
            }

            if (security.SecurityType != SecurityType.Stock &&
                security.SecurityType != SecurityType.Fund &&
                security.SecurityType != SecurityType.Currency)
            {
                throw BuildError(security.Symbol, "Only stocks, funds, and currencies are supported.");
            }

            var symbol = security.Symbol;

            if (security.SecurityType == SecurityType.Currency)
            {
                symbol = symbol + currency.Symbol + "=X";
            }

            var client = new WebClient();
            string data;

            try
            {
                data = client.DownloadString(string.Format(UrlFormat, symbol));
            }
            catch (WebException ex)
            {
                throw BuildError(symbol, "Check the inner exception for details.", ex);
            }

            var split = data.Split(',');

            if (split.Length != 4)
            {
                throw BuildError(symbol, "The data returned was not in a recognized format.");
            }

            var date = Unquote(split[1]);
            var time = Unquote(split[2]);
            var value = Unquote(split[3]);

            if (date == "N/A")
            {
                throw BuildError(symbol, "The symbol could not be found.");
            }

            DateTime utcDate;
            if (!DateTime.TryParse(date, out utcDate))
            {
                throw BuildError(symbol, "The data returned was not in a recognized format.");
            }

            var yahooTimeZone = TimeZoneInfo.FindSystemTimeZoneById(YahooTimeZoneId);

            var minDate = TimeZoneInfo.ConvertTimeFromUtc(utcDate, yahooTimeZone);

            DateTime dateTime;
            if (!DateTime.TryParse(minDate.ToShortDateString() + " " + time, out dateTime))
            {
                throw BuildError(symbol, "The data returned was not in a recognized format.");
            }

            if (dateTime < minDate)
            {
                dateTime.AddDays(1);
            }

            dateTime = TimeZoneInfo.ConvertTimeToUtc(dateTime, yahooTimeZone);

            decimal price;
            if (!decimal.TryParse(value, out price))
            {
                throw BuildError(symbol, "The data returned was not in a recognized format.");
            }

            checked
            {
                var quantity = (long)security.FractionTraded;
                price *= currency.FractionTraded;

                var longPrice = (long)Math.Floor(price);

                while (longPrice != price)
                {
                    price *= 10;
                    quantity *= 10;
                    longPrice = (long)Math.Floor(price);
                }

                var gcd = FinancialMath.GCD(longPrice, quantity / security.FractionTraded);

                quantity /= gcd;
                longPrice /= gcd;

                return new PriceQuote(Guid.NewGuid(), dateTime, security, quantity, currency, longPrice, "Yahoo!® Finance");
            }
        }
Beispiel #13
0
        public void AddSecurity(SecurityData security)
        {
            if (security == null)
            {
                throw new ArgumentNullException("security");
            }

            lock (this)
            {
                var newSecurity = new Security(
                    security.SecurityId,
                    security.SecurityType,
                    security.Name,
                    security.Symbol,
                    security.Format,
                    security.FractionTraded);

                this.destinationBook.AddSecurity(newSecurity);
            }
        }