package org.marketcetera.trade;
import java.math.BigDecimal;
import java.util.regex.Pattern;
import javax.annotation.concurrent.ThreadSafe;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.apache.commons.lang.StringUtils;
import org.marketcetera.core.time.TimeFactory;
import org.marketcetera.core.time.TimeFactoryImpl;
import org.marketcetera.util.misc.ClassVersion;
/* $License$ */
/**
* Represents a Convertible Security instrument.
*
* @version $Id: ConvertibleBond.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@ThreadSafe
@XmlRootElement(name="convertibleBond")
@XmlAccessorType(XmlAccessType.NONE)
@ClassVersion("$Id: ConvertibleBond.java 16901 2014-05-11 16:14:11Z colin $")
public class ConvertibleBond
extends Instrument
{
/**
* Create a new Convertible Security instance.
*
* @param inSymbol a <code>String</code> value
* @throws IllegalArgumentException if the given symbol is <code>null</code>, empty, neither a valid CUSIP nor a valid ISIN nor a valid symbol of the type <code>SYMBOL RATE% DUE-DATE</code>
*/
public ConvertibleBond(String inSymbol)
{
inSymbol = StringUtils.trimToNull(inSymbol);
if(isinPattern.matcher(inSymbol).matches()) {
cusip = getCusipFromIsin(inSymbol);
symbol = cusip;
} else if(cusipPattern.matcher(inSymbol).matches()) {
cusip = inSymbol;
symbol = cusip;
} else if(symbolPattern.matcher(inSymbol).matches()) {
String[] parts = inSymbol.split(" ");
timeFactory.create(parts[2]);
symbol = inSymbol;
cusip = null;
} else {
throw new IllegalArgumentException();
}
}
/**
* Create a new ConvertibleBond instance.
*
* @param inTicker a <code>String</code> value
* @param inCouponRate a <code>BigDecimal</code> value
* @param inMaturity a <code>String</code> value
*/
public ConvertibleBond(String inTicker,
BigDecimal inCouponRate,
String inMaturity)
{
ticker = inTicker;
couponRate = inCouponRate;
maturity = inMaturity;
symbol = inTicker + " " + inCouponRate + "% " + inMaturity;
}
/**
* Gets the CUSIP value.
*
* @return a <code>String</code> value
*/
public String getCusip()
{
return cusip;
}
/* (non-Javadoc)
* @see org.marketcetera.trade.Instrument#getSecurityType()
*/
public SecurityType getSecurityType()
{
return SecurityType.ConvertibleBond;
}
/* (non-Javadoc)
* @see org.marketcetera.trade.Instrument#getSymbol()
*/
@Override
public String getSymbol()
{
return symbol;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("ConvertibleBond [").append(getSymbol()).append("]");
return builder.toString();
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((getSymbol() == null) ? 0 : getSymbol().hashCode());
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ConvertibleBond)) {
return false;
}
ConvertibleBond other = (ConvertibleBond) obj;
if (getSymbol() == null) {
if (other.getSymbol() != null) {
return false;
}
} else if (!getSymbol().equals(other.getSymbol())) {
return false;
}
return true;
}
/**
* Gets the ISIN for this security using the given country code.
*
* @param inCountryCode a <code>String</code> value
* @return a <code>String</code> value
*/
public String getIsin(String inCountryCode)
{
StringBuilder output = new StringBuilder();
output.append(inCountryCode).append(cusip);
int sum = 0;
int index = 0;
for(int i=cusip.length()-1;i>=0;i--) {
char c = cusip.charAt(i);
int asciiValue = (int)c;
if(Character.isLetter(c)) {
asciiValue = ((int)Character.toUpperCase(c)) - 55;
} else {
asciiValue -= 48;
}
if(index++ % 2 == 0) {
asciiValue *= 2;
String asciiValueAsString = String.valueOf(asciiValue);
for(char asciiValueChar : asciiValueAsString.toCharArray()) {
int innerValue = (int)asciiValueChar - 48;
sum += innerValue;
}
} else {
sum += asciiValue;
}
}
int subtrahend = sum;
while(subtrahend % 10 != 0) {
subtrahend += 1;
}
sum = subtrahend - sum;
output.append(sum);
return output.toString();
}
/**
* Get the underlying value.
*
* @return a <code>String</code> value
*/
public String getTicker()
{
return ticker;
}
/**
* Get the coupon rate value.
*
* @return a <code>BigDecimal</code> value
*/
public BigDecimal getCouponRate()
{
return couponRate;
}
/**
* Get the maturity value.
*
* @return a <code>String</code> value
*/
public String getMaturity()
{
return maturity;
}
/**
* Determines the CUSIP from the given ISIN.
*
* @param inIsin a <code>String</code> value
* @return a <code>String</code> value
*/
private static String getCusipFromIsin(String inIsin)
{
// remove the first two letters and the last checksum
return inIsin.substring(2,inIsin.length()-1);
}
/**
* Create a new ConvertibleSecurityImpl instance.
*/
@SuppressWarnings("unused")
private ConvertibleBond() {}
/**
* symbol value
*/
@XmlAttribute
private String symbol;
/**
* ticker value
*/
@XmlAttribute
private String ticker;
/**
* rate value
*/
@XmlAttribute
private BigDecimal couponRate;
/**
* maturity value
*/
@XmlAttribute
private String maturity;
/**
* cusip value
*/
@XmlAttribute
private String cusip;
/**
* isin regex
*/
public static final Pattern isinPattern = Pattern.compile("^[A-Z]{2}([A-Z0-9]){9}[0-9]$");
/**
* cusip regex
*/
public static final Pattern cusipPattern = Pattern.compile("^[0-9]{3}[A-Z0-9]{5}[0-9]$");
/**
* pattern used to validate symbols of the type "ticker rate maturity", as in "IBM 2.54% 03/15/2014"
*/
public static final Pattern symbolPattern = Pattern.compile("^\\w* \\d{1}(\\.{1}\\d{1,3}){0,1}% \\d{1,2}/\\d{1,2}/\\d{1,4}$");
/**
* converts due dates as necessary
*/
private static final TimeFactory timeFactory = new TimeFactoryImpl();
private static final long serialVersionUID = -7797829861069074193L;
}