Formatting Yaxis prices for futures, ie "123-16" not "123.5"

Discussion about JFreeChart related to stockmarket charts.
Locked
snid3ly
Posts: 23
Joined: Wed Aug 07, 2013 4:21 am
antibot: No, of course not.

Formatting Yaxis prices for futures, ie "123-16" not "123.5"

Post by snid3ly » Wed Aug 14, 2013 12:25 am

I need to format the prices on a vertical axis as treasury futures are quoted in 32nd of a point. So instead of showing 123.5 I will show 123-16. And instead of showing 97.5703125, I will show 97-182

http://www.cmegroup.com/education/files ... utures.pdf

What's the best way to do this?

Thanks!

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Re: Formatting Yaxis prices for futures, ie "123-16" not "12

Post by david.gilbert » Wed Aug 14, 2013 1:36 pm

You need a custom NumberFormat subclass. As an example, you could look at the HexNumberFormat class in the org.jfree.chart.util.* package. If you get something working, it would be a great addition to JFreeChart if you want to make it available for inclusion in the library.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

snid3ly
Posts: 23
Joined: Wed Aug 07, 2013 4:21 am
antibot: No, of course not.

Re: Formatting Yaxis prices for futures, ie "123-16" not "12

Post by snid3ly » Wed Aug 14, 2013 8:42 pm

I'd be happy to contribute a futures price formatter. I expect it would throw "InvalidFuturesPrice" if someone tried to format a number which was not a valid futures price

But how do I make sure ticks lines on the vertical axis are only displayed at values representing valid futures price values? fractional portions must be some fraction of a 32nd (up to quarters of 32nds for some products) ie:

123.0 = 123-000 // OK
123.0078125 = 123-002 //OK
123.015625 = 123-005 // OK
...
123.4 // NOT OK invalid futures price
123.5 = 123-16 or 123-160 // OK
123.6 // NOT OK invalid futures price

Thanks!

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Re: Formatting Yaxis prices for futures, ie "123-16" not "12

Post by david.gilbert » Thu Aug 15, 2013 9:37 am

You could set the tick size to some multiple of 1/128. I guess you might need to take care of some small rounding errors while doing the number formatting.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

snid3ly
Posts: 23
Joined: Wed Aug 07, 2013 4:21 am
antibot: No, of course not.

Re: Formatting Yaxis prices for futures, ie "123-16" not "12

Post by snid3ly » Fri Aug 16, 2013 5:21 am

Here it is...

Code: Select all

/*
 *
 * -----------------------
 * TreasuryPriceFormat.java
 * -----------------------
 * 
 */

// package org.jfree.chart.util;

import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.HashMap;

/**
 * A custom number formatter that formats futures
 * prices in 32nds of a point as described here:
 * <p/>
 * http://en.wikipedia.org/wiki/Commodity_tick
 * and here:
 * http://www.cmegroup.com/trading/interest-rates/files/IR147_USTreasuryRevised_Final_low_client.pdf
 *
 */
public class TreasuryPriceFormat extends NumberFormat {

    private boolean decimalIncluded;
    private boolean trailingZeroIncluded;

    private HashMap<String, String> m = new HashMap<String, String>();

    /**
     * Creates a new instance. if {@code trailingZeroIncluded} is false {@code decimalIncluded}
     * is ignored.
     *
     * @param decimalIncluded      Specify if a decimal should
     *                             appear prior to any fraction of a 32nd.  ie.,
     *                             123.3125 could be formatted as 123-100 or 123-10.0
     *
     * @param trailingZeroIncluded Specify if a trailing 0 should
     *                             appear after a whole 32nd.  ie.,
     *                             123.3125 could be formatted as 123-100 or 123-10.
     *                             This should likely be {@code false} for displaying
     *                             30 year Treasury bond prices.
     */
    public TreasuryPriceFormat(boolean decimalIncluded, boolean trailingZeroIncluded) {
        super();
        this.decimalIncluded = decimalIncluded;
        this.trailingZeroIncluded = trailingZeroIncluded;
        populateHashMap();
    }

    /**
     * Returns if a trailing 0 will be shown.
     *
     * @return if a trailing 0 will be shown.
     */
    public final boolean isTrailingZeroIncluded() {
        return this.trailingZeroIncluded;
    }

    /**
     * Returns if a decimal is included in formatted output.
     *
     * @return usingDecimal
     */
    public final boolean isDecimalIncluded() {
        return this.decimalIncluded;
    }

    @Override
    public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
        return null;
    }


    /**
     * Formats the specified double as a futures price in 32nds of a point
     *
     * @param number     the number to format.
     * @param toAppendTo the buffer to append to (ignored here).
     * @param pos        the field position (ignored here).
     * @return The string buffer.
     */
    public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
        return format(number + "", toAppendTo, pos);
    }

    /**
     * Formats the specified String as a futures price in 32nds of a point
     *
     * @param number     the number to format.
     * @param toAppendTo the buffer to append to (ignored here).
     * @param pos        the field position (ignored here).
     * @return The string buffer.
     */
    public StringBuffer format(String number, StringBuffer toAppendTo, FieldPosition pos) {
        StringBuffer sb = new StringBuffer();

        int decimalIndex = number.indexOf('.');

        if (decimalIndex == -1) {
            sb.append(number);
            noFraction(sb);
        } else {
            String whole = number.substring(0, decimalIndex);
            String frac = number.substring(decimalIndex + 1);

            frac = frac.trim();

            //remove trailing zeros.
            while (frac.endsWith("0")) frac = frac.substring(0, frac.length() - 1);

            sb.append(whole);

            if (frac.length() == 0) {
                noFraction(sb);
            } else {
                String s = m.get(frac);
                if (s == null) throw new NumberFormatException("Invalid futures price " + number + ".");
                sb.append(s);
            }
        }

        return sb;
    }

    private void noFraction(StringBuffer sb) {
        if (trailingZeroIncluded) {
            if (decimalIncluded) {
                sb.append("-00.0");
            } else {
                sb.append("-000");
            }
        } else {
            sb.append("-00");
        }
    }

    /**
     * Parsing is not implemented, so this method always returns
     * <code>null</code>.
     *
     * @param source        ignored.
     * @param parsePosition ignored.
     * @return Always <code>null</code>.
     */
    public Number parse(String source, ParsePosition parsePosition) {
        return null;
    }

    private String f(String s) {
        if (trailingZeroIncluded) {
            if (decimalIncluded) {
                return s;
            } else {
                return s.replace(".", "");
            }
        } else {
            return s.replace(".0", "");
        }
    }

    private void populateHashMap() {

        m.put("0078125", f("-00.2"));
        m.put("015625", f("-00.5"));
        m.put("0234375", f("-00.7"));
        m.put("03125", f("-01.0"));
        m.put("0390625", f("-01.2"));
        m.put("046875", f("-01.5"));
        m.put("0546875", f("-01.7"));
        m.put("0625", f("-02.0"));
        m.put("0703125", f("-02.2"));
        m.put("078125", f("-02.5"));
        m.put("0859375", f("-02.7"));
        m.put("09375", f("-03.0"));
        m.put("1015625", f("-03.2"));
        m.put("109375", f("-03.5"));
        m.put("1171875", f("-03.7"));
        m.put("125", f("-04.0"));
        m.put("1328125", f("-04.2"));
        m.put("140625", f("-04.5"));
        m.put("1484375", f("-04.7"));
        m.put("15625", f("-05.0"));
        m.put("1640625", f("-05.2"));
        m.put("171875", f("-05.5"));
        m.put("1796875", f("-05.7"));
        m.put("1875", f("-06.0"));
        m.put("1953125", f("-06.2"));
        m.put("203125", f("-06.5"));
        m.put("2109375", f("-06.7"));
        m.put("21875", f("-07.0"));
        m.put("2265625", f("-07.2"));
        m.put("234375", f("-07.5"));
        m.put("2421875", f("-07.7"));
        m.put("25", f("-08.0"));
        m.put("2578125", f("-08.2"));
        m.put("265625", f("-08.5"));
        m.put("2734375", f("-08.7"));
        m.put("28125", f("-09.0"));
        m.put("2890625", f("-09.2"));
        m.put("296875", f("-09.5"));
        m.put("3046875", f("-09.7"));
        m.put("3125", f("-10.0"));
        m.put("3203125", f("-10.2"));
        m.put("328125", f("-10.5"));
        m.put("3359375", f("-10.7"));
        m.put("34375", f("-11.0"));
        m.put("3515625", f("-11.2"));
        m.put("359375", f("-11.5"));
        m.put("3671875", f("-11.7"));
        m.put("375", f("-12.0"));
        m.put("3828125", f("-12.2"));
        m.put("390625", f("-12.5"));
        m.put("3984375", f("-12.7"));
        m.put("40625", f("-13.0"));
        m.put("4140625", f("-13.2"));
        m.put("421875", f("-13.5"));
        m.put("4296875", f("-13.7"));
        m.put("4375", f("-14.0"));
        m.put("4453125", f("-14.2"));
        m.put("453125", f("-14.5"));
        m.put("4609375", f("-14.7"));
        m.put("46875", f("-15.0"));
        m.put("4765625", f("-15.2"));
        m.put("484375", f("-15.5"));
        m.put("4921875", f("-15.7"));
        m.put("5", f("-16.0"));
        m.put("5078125", f("-16.2"));
        m.put("515625", f("-16.5"));
        m.put("5234375", f("-16.7"));
        m.put("53125", f("-17.0"));
        m.put("5390625", f("-17.2"));
        m.put("546875", f("-17.5"));
        m.put("5546875", f("-17.7"));
        m.put("5625", f("-18.0"));
        m.put("5703125", f("-18.2"));
        m.put("578125", f("-18.5"));
        m.put("5859375", f("-18.7"));
        m.put("59375", f("-19.0"));
        m.put("6015625", f("-19.2"));
        m.put("609375", f("-19.5"));
        m.put("6171875", f("-19.7"));
        m.put("625", f("-20.0"));
        m.put("6328125", f("-20.2"));
        m.put("640625", f("-20.5"));
        m.put("6484375", f("-20.7"));
        m.put("65625", f("-21.0"));
        m.put("6640625", f("-21.2"));
        m.put("671875", f("-21.5"));
        m.put("6796875", f("-21.7"));
        m.put("6875", f("-22.0"));
        m.put("6953125", f("-22.2"));
        m.put("703125", f("-22.5"));
        m.put("7109375", f("-22.7"));
        m.put("71875", f("-23.0"));
        m.put("7265625", f("-23.2"));
        m.put("734375", f("-23.5"));
        m.put("7421875", f("-23.7"));
        m.put("75", f("-24.0"));
        m.put("7578125", f("-24.2"));
        m.put("765625", f("-24.5"));
        m.put("7734375", f("-24.7"));
        m.put("78125", f("-25.0"));
        m.put("7890625", f("-25.2"));
        m.put("796875", f("-25.5"));
        m.put("8046875", f("-25.7"));
        m.put("8125", f("-26.0"));
        m.put("8203125", f("-26.2"));
        m.put("828125", f("-26.5"));
        m.put("8359375", f("-26.7"));
        m.put("84375", f("-27.0"));
        m.put("8515625", f("-27.2"));
        m.put("859375", f("-27.5"));
        m.put("8671875", f("-27.7"));
        m.put("875", f("-28.0"));
        m.put("8828125", f("-28.2"));
        m.put("890625", f("-28.5"));
        m.put("8984375", f("-28.7"));
        m.put("90625", f("-29.0"));
        m.put("9140625", f("-29.2"));
        m.put("921875", f("-29.5"));
        m.put("9296875", f("-29.7"));
        m.put("9375", f("-30.0"));
        m.put("9453125", f("-30.2"));
        m.put("953125", f("-30.5"));
        m.put("9609375", f("-30.7"));
        m.put("96875", f("-31.0"));
        m.put("9765625", f("-31.2"));
        m.put("984375", f("-31.5"));
        m.put("9921875", f("-31.7"));

    }


    /**
     * Returns a new {@code TreasuryPriceFormat} with the same
     * properties as this {@code TreasuryPriceFormat}.
     */
    @Override
    public Object clone() {
        TreasuryPriceFormat clone = (TreasuryPriceFormat) super.clone();
        clone.decimalIncluded = decimalIncluded;
        clone.trailingZeroIncluded = trailingZeroIncluded;
        return clone;
    }
}

Feel free to modify it or add it if you think it would help helpful.

Thanks!

Locked