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; }
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; }
public Balance(Security security, long amount, bool isExact) { if (security == null) { throw new ArgumentNullException("security"); } this.security = security; this.amount = amount; this.isExact = isExact; }
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; }
/// <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); }
/// <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()); }
/// <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)); }
/// <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"); } }
public SecurityOption(Security security) { this.security = security; }
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"); } }
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); } }