3

Currently I'm writing a java program to find out on which day a Hammer or other Candlestick pattern formed.

The user has to input 2 dates as arguments when executing the program, e.g. java ReadingTest 2016-09-03 2016-10-31, and the program will look for Hammer pattern from 2016-09-03 to 2016-10-31.

The code is as follows:

import java.io.*;
import java.util.*;
import java.text.*;

public class ReadingTest
{
    public static void main(String[] args) throws IOException,ParseException
    {
    //Import file to java
    File file = new file("table.csv");

    //Read the file
    Scanner infile = new Scanner(file);

    //Skip the first line in table.csv
    infile.nextLine();

    //Define format of date
    SImpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    //Name the variables user enters
    Date start = sdf.parse(args[0]);
    Date end = sdf.parse(args[1]);

    //Create ArrayList for each column of data
    ArrayList<String> date = new ArrayList<String>();
    ArrayList<Double> open = new ArrayList<Double>();
    ArrayList<Double> high = new ArrayList<Double>();
    ArrayList<Double> low = new ArrayList<Double>();
    ArrayList<Double> close = new ArrayList<Double>();

    while (infile.hasNext())
    {
        //Tokenize columns by comma
        String[] data = infile.nextLine().split(",");
        //Organize each column of data to one index of data array
        date.add(data[0]);
        open.add(Double.parseDouble(data[1]));
        high.add(Double.parseDouble(data[2]));
        low.add(Double.parseDouble(data[3]));
        close.add(Double.parseDouble(data[4]));
    }
    //Show options and ask user to choose
    System.out.println("1. Hammer");
    System.out.println("2. Three white soldiers");
    System.out.println("3. Bullish kicker");

    //Record user input and execute corresponding code
    Scanner input = new Scanner(System.in);
    int choice = input.nextInt();

    if (choice == 1)
        for (int i = 0; i < date.size(); i++)
            if (close.get(i) > open.get(i) &&
                close.get(i) > ((high.get(i)) + (low.get(i)))/2 &&
                ((close.get(i) - low.get(i))/2 > (high.get(i) - close.get(i)))
            System.out.println("Pattern found: " + date.get(i));
}
}

The code works perfectly till here. However, the output in the last line of code is in the dd/MM/yyyy format and I tried to use sdf.parse(date.get(i)) instead of date.get(i) to show results in yyyy-MM-dd format. Running the code with sdf.parse(date.get(i)) returns the following error:

Exception in thread "main" java.text.ParseException: Unparseable date: 
"25/10/2016" at 
java.text.DateFormat.parse(Unknown source) at ReadingTest.main(ReadingTest.java:59)

I also tried to display only dates showing a Hammer using:

(date.get(i).after(start) && date.get(i).before(end))

and results in

error: cannot find symbol
symbol: method before(Date)
location: class String

And the CSV file looks like this:

Date       Open  High  Low  Close  
31/10/2016 58.25 58.65 58.2 58.35
28/10/2016 58.95 59    58.3 58.35
.
.
.
1/8/2016   50.8  51.1 50.75  50.8

How the code should be amended to make it work?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Z_Z
  • 33
  • 4
  • 2
    Your code is not compiling due to some basic typo(i.e. the missing bracket at the beginning of the main function). Try to give a proper code sample next time. – BaptisteL Nov 07 '16 at 16:10
  • 1
    By the way, you are using old date-time classes, now legacy, supplanted by the java.time classes. – Basil Bourque Nov 07 '16 at 16:31

3 Answers3

3

I guess what you want is this

SimpleDateFormat hammerFormat = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat slashFormat = new SimpleDateFormat("dd/MM/yyyy");

so you can parse your date to the yyyy-MM-dd representation like this

hammerFormat.format(slashFormat.parse(date.get(i))));

Full Code

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Scanner;

public class ReadingTest {
    public static void main(String[] args) throws IOException, ParseException {
        // Import file to java
        File file = new File("table.csv");

        // Read the file
        Scanner infile = new Scanner(file);

        // Skip the first line in table.csv
        infile.nextLine();

        // Define format of date
        SimpleDateFormat hammerFormat = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat slashFormat = new SimpleDateFormat("dd/MM/yyyy");

        // Name the variables user enters
        Date start = hammerFormat.parse(args[0]);
        Date end = hammerFormat.parse(args[1]);

        // Create ArrayList for each column of data
        ArrayList < String > date = new ArrayList < String > ();
        ArrayList < Double > open = new ArrayList < Double > ();
        ArrayList < Double > high = new ArrayList < Double > ();
        ArrayList < Double > low = new ArrayList < Double > ();
        ArrayList < Double > close = new ArrayList < Double > ();

        while (infile.hasNext()) {
            // Tokenize columns by comma
            String[] data = infile.nextLine().split(",");
            // Organize each column of data to one index of data array
            date.add(data[0]);
            open.add(Double.parseDouble(data[1]));
            high.add(Double.parseDouble(data[2]));
            low.add(Double.parseDouble(data[3]));
            close.add(Double.parseDouble(data[4]));
        }
        // Show options and ask user to choose
        System.out.println("1. Hammer");
        System.out.println("2. Three white soldiers");
        System.out.println("3. Bullish kicker");

        // Record user input and execute corresponding code
        Scanner input = new Scanner(System.in);
        int choice = input.nextInt();

        if (choice == 1)
            for (int i = 0; i < date.size(); i++)
                if (close.get(i) > open.get(i) && close.get(i) > ((high.get(i)) + (low.get(i))) / 2 && ((close.get(i) - low.get(i)) / 2 > (high.get(i) - close.get(i))))
                    System.out.println("Pattern found: " + hammerFormat.format(slashFormat.parse(date.get(i))));
    }
}

Edit:

With a .csv file format like this (because in the code it says .split(","))

31/10/2016, 58.25, 58.65, 58.20, 58.35
28/10/2016, 58.95, 59.00, 58.30, 58.35
01/08/2016, 50.80, 51.10, 50.75, 50.80

It works fine for me. I passed the two arguments 2016-09-03 2016-10-31 when executing the program.

ncw
  • 1,635
  • 1
  • 18
  • 26
  • 1
    there is an error saying exception in thread "main" java.lang.ArrayIndexOutOfBoundException: 0 at ReadingTest.main even the ArrayOutOfBounds Exceptiono is added – Z_Z Nov 07 '16 at 16:28
  • 1
    @Z_Z you have to pass the two arguments when executing the program. Also see the edit in my answer – ncw Nov 07 '16 at 17:31
2

The Format in the file has NOT the Format "yyyy-MM-dd", it has the Format "dd/MM/yyyy", so you cannot parse it with your variable sdf. You could define a second date format

SImpleDateFormat sdfParse = new SimpleDateFormat("dd/MM/yyyy");

and then do parsing with this and formatting with yours:

sdf.format(sdfParse.parse(date.get(i)))

to hopefully get a better result.

Thomas Philipp
  • 261
  • 1
  • 5
1

tl;dr

Parsing Date

Use only java.time classes for date-time work.

LocalDate start = LocalDate.parse( "2018-10-27" );  // Parse input string as a date-only value (no time-of-day, no time zone), a `java.time.LocalDate` object.

… in an if-statement and showing only results in stipulated time range

if ( 
    ( ! stockDay.getDate().isBefore( start ) )     // A shorter way to ask "is equal to OR later" is "is NOT before". 
    && 
    stockDay.getDate().isBefore( stop )            // Half-Open approach, where beginning is *inclusive* while the ending is *exclusive*.
) { … }

Even better, represent your start-stop date range using the LocalDateRange class found in ThreeTen-Extra library.

if ( 
     LocalDateRange.of( start , stop )
                   .contains( LocalDate.parse( "2018-10-27" ) )
) { … }

Details

How the code should be amended to make it work?

You may be sorry you asked. I went a little nuts with this Answer, being curious about Candlestick Hammer, and wanting to work more with Java Streams.

Fix the date-time handling

You are using troublesome old date-time classes that were supplanted years ago but the java.time classes.

Furthermore, you are abusing those legacy classes. You are putting a date-only value in a date-with-time-of-day type. Use java.time.LocalDate instead, for a date-only value.

You failed to define a formatting pattern to match your inputs. Your pattern says "yyyy-MM-dd" but your inputs are in day-month-year order rather than year-month-day order. Define a formatting pattern to match your string inputs. Use the modern class, DateTimeFormatter.

DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu" ) ;

Parse your input string.

LocalDate ld = LocalDate.parse( "31/10/2016" , f ) ;

ld.toString(): 2018-10-31

Be clear that a date-time object has no “format”. Formats relate to text representing a date-time value/object, to the String object being generated from a date-time object. But the date-time object and the String remain separate and distinct.

By the way, do not use such a custom or localized format for your inputs. Always use standard ISO 8601 formats when exchanging date-time values as text. The java.time classes use these standard formats by default when parsing/generating strings.

Define a class for data-model

Rather than mess with arrays or a bunch of List objects, define a class for your data. Name it something like StockDay to represent a stock’s performance on a particular date.

Never use double or Double, nor float or Float, to represent money. These types use floating-point technology that trades away accuracy to get faster execution performance. Instead use the BigDecimal class; slower, but accurate.

On this class we include the business logic in defining a Candlestick Hammer. For simplicity, I went with the stricter definition disallowing an upper “shadow”. All this code is for demonstration-only, completely untested: Use at your own risk.

package com.basilbourque.example;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Objects;

public class StockDay implements Comparable < StockDay > {

    // Example data:
    // Date       Open  High  Low  Close
    // 31/10/2016 58.25 58.65 58.2 58.35

    // ---------|  Members  |------------------------
    private LocalDate date;
    private BigDecimal open, high, low, close;
    private Boolean isHammer;

    // ---------|  Constructors  |------------------------
    public StockDay ( final LocalDate localDateArg , final BigDecimal openArg , final BigDecimal highArg , final BigDecimal lowArg , final BigDecimal closeArg ) {
        // In real work, add defensive code to validate data such as no nulls, only positive numbers, reasonable dates.
        this.date = localDateArg;
        this.open = openArg;
        this.high = highArg;
        this.low = lowArg;
        // Verify the high is greater than or equal to the low.
        if ( this.high.compareTo( this.low ) < 0 ) {
            throw new IllegalArgumentException( "The passed High is below the passed Low for Date of " + this.date + ". Not possible." );
        }
        this.close = closeArg;
        this.isHammer = this.determineHammer();
    }

    private Boolean determineHammer () {
        // A hammer is a price pattern in candlestick charting that occurs when a security trades significantly lower than its opening, but rallies later in the day to close either above or near its opening price. This pattern forms a hammer-shaped candlestick, in which the body is at least half the size of the tail or wick.
        // Read more: Hammer https://www.investopedia.com/terms/h/hammer.asp#ixzz5G6rqtbkv
        // See also: http://www.onlinetradingconcepts.com/TechnicalAnalysis/Candlesticks/Hammer.html

        // Caveat: This code is a quick rough draft, not thought-through, and totally untested. Use at your own risk. For demonstration purposes only.

        // First check if the High is above the Close. A Hammer has little or no upper  "shadow" (line protruding above the box). We'll go with "no shadow" for simplicity here.
        if ( this.high.compareTo( this.close ) > 0 ) { // if high > close, not a hammer.
            return Boolean.FALSE;
        }

        // Proceed with next check: Is "tail" (lower shadow) at least twice as long as height of box.
        BigDecimal closeOpenDeltaAbsolute_BoxHeight = this.close.subtract( this.open ).abs();
        BigDecimal lowerOfCloseOrOpen = ( this.close.compareTo( this.open ) < 0 ) ? this.close : this.open;  // If x is smaller than y, use x. If x is greater than or equal to y, use y.
        BigDecimal lowerShadowHeight = lowerOfCloseOrOpen.subtract( this.low );
        // A Hammer has a long lower shadow (delta between either Close or Open, whichever is lower, and the Low), at least twice as tall as the box (Close-Open absolute delta).
        BigDecimal requiredMinimumLengthFactorOfShadow = new BigDecimal( "2" );
        BigDecimal doubleCloseOpenDeltaAbsolute = closeOpenDeltaAbsolute_BoxHeight.multiply( requiredMinimumLengthFactorOfShadow );
        Boolean hammer = ( lowerShadowHeight.compareTo( doubleCloseOpenDeltaAbsolute ) > 0 );
        return hammer;

    }


    // ---------|  Accessors  |------------------------
    // All fields are read-only. Just getters, no setters.
    public LocalDate getDate () {
        return this.date;
    }

    public BigDecimal getOpen () {
        return this.open;
    }

    public BigDecimal getHigh () {
        return this.high;
    }

    public BigDecimal getLow () {
        return this.low;
    }

    public BigDecimal getClose () {
        return this.close;
    }

    public Boolean isHammer () {
        return this.isHammer;
    }

    // ---------|  Override `Object`  |------------------------
    @Override
    public String toString () {
        return "StockDay{ " +
                "date=" + this.date +
                ", open=" + this.open +
                ", high=" + this.high +
                ", low=" + this.low +
                ", close=" + this.close +
                ", isHammer=" + this.isHammer +
                " }";
    }

    @Override
    public boolean equals ( final Object oArg ) {
        if ( this == oArg ) return true;
        if ( oArg == null || getClass() != oArg.getClass() ) return false;
        final StockDay stockDay = ( StockDay ) oArg;
        return Objects.equals( this.date , stockDay.date ) &&
                Objects.equals( this.open , stockDay.open ) &&
                Objects.equals( this.high , stockDay.high ) &&
                Objects.equals( this.low , stockDay.low ) &&
                Objects.equals( this.close , stockDay.close );
        // Perhaps this should be coded to only consider the `LocalDate` field alone.
    }

    @Override
    public int hashCode () {
        return Objects.hash( this.date , this.open , this.high , this.low , this.close );
        // Perhaps this should be coded to only consider the `LocalDate` field alone.
    }


    @Override
    public int compareTo ( final StockDay o ) {
        // Compare the date field only.
        int result = this.getDate().compareTo( o.getDate() );
        return result;
    }
}

Define a class for loading data

Create a class to load your CSV data.

package com.basilbourque.example;

import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class StockDayLoader {
    final private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );  // Tip: Instead of this custom format, use standard ISO 8601 formats when exchanging date-time values as text.

    public List < StockDay > loadFrom ( final Reader input ) {
        final List < StockDay > stockDays = new ArrayList <>();  // To be populated by lines of data read from the input.

        try (  // try-with-resources
               Scanner scanner = new Scanner( input ).useDelimiter( "\r\n" ) ; // Delimiter is a comma between fields.
        ) {
            scanner.useLocale( Locale.US ); // Determines cultural norms such as FULL STOP versus COMMA for decimal point in a `BigDecimal`.

            // Skip first line, the column headers.
            if ( scanner.hasNextLine() ) {
                String headers = scanner.nextLine(); // Ignore returned String.
                if ( ! "Date,Open,High,Low,Close".equals( headers ) ) { // Verify expected input.
                    throw new IllegalArgumentException( "The passed Readable object’s first row does not consist of expected column header names." );
                }
            }

            while ( scanner.hasNextLine() ) {
                String line = scanner.nextLine(); // Grab entire line.
                try (
                        Scanner lineScanner = new Scanner( line ).useDelimiter( "," ).useLocale( Locale.US ) ;
                ) {
                    String dateInput = lineScanner.next();
                    LocalDate date = LocalDate.parse( dateInput , this.dateFormatter );
                    try {
                        BigDecimal open = lineScanner.nextBigDecimal();
                        BigDecimal high = lineScanner.nextBigDecimal();
                        BigDecimal low = lineScanner.nextBigDecimal();
                        BigDecimal close = lineScanner.nextBigDecimal();
                        StockDay stockDay = new StockDay( date , open , high , low , close );
                        stockDays.add( stockDay );  // Collect the newly intanstiated `StockDay` object.
                    } catch ( InputMismatchException e ) {
                        System.out.println( "ERROR The next token does not match the Decimal regular expression, or is out of range.  " );
                        e.printStackTrace();
                    }
                }
            }

            return stockDays;
        }
    }


    // -----------|  Testing/Demo  |------------------------

    public static String bogusData () {
        final String eol = "\r\n";   // RFC 4180 requires CrLf as end-of-line.
        final StringBuilder sb = new StringBuilder();
        sb.append( "Date,Open,High,Low,Close" + eol );
        sb.append( "31/10/2016,58.25,58.65,58.2,58.35" + eol );
        sb.append( "28/10/2016,58.95,59,58.3,58.35" + eol );
        sb.append( "27/10/2016,58.78,58.22,33.3,58.55" + eol ); // Hammer.
        sb.append( "26/10/2016,58.95,59.05,58.43,58.45" + eol );
        sb.append( "25/10/2016,58.99,58.44,22.2,58.57" + eol ); // Hammer.
        String s = sb.toString();
        return s;
    }

    public static void main ( String[] args ) {
        String s = StockDayLoader.bogusData();
        Reader reader = new StringReader( s );

        StockDayLoader loader = new StockDayLoader();
        List < StockDay > list = loader.loadFrom( reader );

        System.out.println( list );
    }

}

Apache Commons CSV

Even better, rather than write your own CSV reader, leverage existing well-used well-tested code found in a library such as Apache Commons CSV.

package com.basilbourque.example;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

public class StockDayLoaderEasy {

    final private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );  // Tip: Instead of this custom format, use standard ISO 8601 formats when exchanging date-time values as text.

    public List < StockDay > loadFrom ( final Reader reader ) {
        final List < StockDay > stockDays = new ArrayList <>();  // To be populated by lines of data read from the input.

        Iterable < CSVRecord > records = null;
        try {
            records = CSVFormat.RFC4180.parse( reader );
        } catch ( IOException eArg ) {
            eArg.printStackTrace();
        }

        // Read each column. Names:  "Date,Open,High,Low,Close"
        for ( CSVRecord record : records ) {
            LocalDate date = LocalDate.parse( record.get( "Date" ) , this.dateFormatter );
            BigDecimal open = new BigDecimal( record.get( "Open" ) );
            BigDecimal high = new BigDecimal( record.get( "High" ) );
            BigDecimal low = new BigDecimal( record.get( "Low" ) );
            BigDecimal close = new BigDecimal( record.get( "Close" ) );

            StockDay stockDay = new StockDay( date , open , high , low , close );
            stockDays.add( stockDay );  // Collect the newly intanstiated `StockDay` object.
        }
        return stockDays;
    }

    // -----------|  Testing/Demo  |------------------------

    public static String bogusData () {
        final String eol = "\r\n";   // RFC 4180 requires CrLf as end-of-line.
        final StringBuilder sb = new StringBuilder();
        sb.append( "Date,Open,High,Low,Close" + eol );
        sb.append( "31/10/2016,58.25,58.65,58.2,58.35" + eol );
        sb.append( "28/10/2016,58.95,59,58.3,58.35" + eol );
        sb.append( "27/10/2016,58.78,58.22,33.3,58.55" + eol ); // Hammer.
        sb.append( "26/10/2016,58.95,59.05,58.43,58.45" + eol );
        sb.append( "25/10/2016,58.99,58.44,22.2,58.57" + eol ); // Hammer.
        String s = sb.toString();
        return s;
    }

    public static void main ( String[] args ) {
        String s = StockDayLoader.bogusData();
        Reader reader = new StringReader( s );

        StockDayLoader loader = new StockDayLoader();
        List < StockDay > list = loader.loadFrom( reader );

        System.out.println( list );
    }
}

Pull it all together

Create an app to exercise these pieces.

The goal of the Question was to find which stock-day-reports met the criteria of (a) date within a date-range, AND (b) is a Hammer.

Several short examples are presented here. Some use old-fashioned Java syntax, and some use modern Streams/Lambda syntax, with same results either way. Read the code-comments for guidance.

package com.basilbourque.example;

import org.threeten.extra.LocalDateRange;

import java.io.Reader;
import java.io.StringReader;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class HammerTime {
    public static void main ( String[] args ) {
        HammerTime hammerTime = new HammerTime();
        hammerTime.doIt();
    }

    private void doIt () {
        // Load all data.
        Reader reader = new StringReader( StockDayLoader.bogusData() );
        List < StockDay > stockDays = new StockDayLoader().loadFrom( reader );
        Collections.sort( stockDays );  // Sort chronologically, ascending order, oldest first. For newest first, call `Collections.reverse`.
        System.out.println( "All stockDays = " + stockDays );

        // Find hammers using the old-fashioned way.
        List < StockDay > hammers = new ArrayList <>();
        for ( StockDay stockDay : stockDays ) {
            if ( stockDay.isHammer() ) {
                hammers.add( stockDay );
            }
        }
        System.out.println( "hammers: " + hammers );

        // Find hammers using modern Streams/Lambda features, while tolerating NULLs.
        List < StockDay > hammers2 = stockDays.stream()
                .filter( stockDay -> Objects.equals( stockDay.isHammer() , Boolean.TRUE ) )  // Use `Objects.equals` to tolerate NULL values.
                .collect( Collectors.toList() );
        System.out.println( "hammers2: " + hammers2 );

        // Find hammers using modern Streams/Lambda features, assuming no NULL values exist.
        List < StockDay > hammers3 = stockDays.stream()
                .filter( stockDay -> stockDay.isHammer().equals( Boolean.TRUE ) )  // Simpler syntax than above, if you are certain of no NULLs.
                .collect( Collectors.toList() );
        System.out.println( "hammers3: " + hammers3 );

        // Find hammers within a certain date-range, per the original Question.

        // Parse the user’s input start/stop dates.
        LocalDate start = LocalDate.parse( "2016-10-26" ); // This range should pick up the hammer on the 27th while omitting the hammer on the 25th.
        LocalDate stop = LocalDate.parse( "2016-10-28" ); // Usual practice in defining a span-of-time is the Half-Open approach, where the beginning is *inclusive* while the ending is *exclusive*.

        // Find hammers within date range using the old-fashioned syntax, with built-in classes.
        List < StockDay > hammersInDateRange1 = new ArrayList <>();
        for ( StockDay stockDay : stockDays ) {
            if ( stockDay.isHammer() ) {
                if ( ( ! stockDay.getDate().isBefore( start ) ) && stockDay.getDate().isBefore( stop ) ) {
                    hammersInDateRange1.add( stockDay );
                }
            }
        }
        System.out.println( "hammersInDateRange1: " + hammersInDateRange1 );

        // Find hammers within date range using the old-fashioned syntax, with the ThreeTen-Extra library and its `LocalDateRange` class.  http://www.threeten.org/threeten-extra/
        final LocalDateRange dateRange = LocalDateRange.of( start , stop );
        List < StockDay > hammersInDateRange2 = new ArrayList <>();
        for ( StockDay stockDay : stockDays ) {
            if ( stockDay.isHammer() ) {
                if ( dateRange.contains( stockDay.getDate() ) ) {
                    hammersInDateRange2.add( stockDay );
                }
            }
        }
        System.out.println( "hammersInDateRange2: " + hammersInDateRange2 );

        // Find hammers within date range using modern Streams/Lambda syntax, with the ThreeTen-Extra library and its `LocalDateRange` class.  http://www.threeten.org/threeten-extra/
        List < StockDay > hammersInDateRange3 = stockDays.stream()
                .filter( stockDay -> stockDay.isHammer() && dateRange.contains( stockDay.getDate() ) )  // Assumes no NULLs.
                .collect( Collectors.toList() );
        System.out.println( "hammersInDateRange3: " + hammersInDateRange3 );


    }
}

When run.

All stockDays = [StockDay{ date=2016-10-25, open=58.99, high=58.44, low=22.2, close=58.57, isHammer=true }, StockDay{ date=2016-10-26, open=58.95, high=59.05, low=58.43, close=58.45, isHammer=false }, StockDay{ date=2016-10-27, open=58.78, high=58.22, low=33.3, close=58.55, isHammer=true }, StockDay{ date=2016-10-28, open=58.95, high=59, low=58.3, close=58.35, isHammer=false }, StockDay{ date=2016-10-31, open=58.25, high=58.65, low=58.2, close=58.35, isHammer=false }]

hammers: [StockDay{ date=2016-10-25, open=58.99, high=58.44, low=22.2, close=58.57, isHammer=true }, StockDay{ date=2016-10-27, open=58.78, high=58.22, low=33.3, close=58.55, isHammer=true }]

hammers2: [StockDay{ date=2016-10-25, open=58.99, high=58.44, low=22.2, close=58.57, isHammer=true }, StockDay{ date=2016-10-27, open=58.78, high=58.22, low=33.3, close=58.55, isHammer=true }]

hammers3: [StockDay{ date=2016-10-25, open=58.99, high=58.44, low=22.2, close=58.57, isHammer=true }, StockDay{ date=2016-10-27, open=58.78, high=58.22, low=33.3, close=58.55, isHammer=true }]

hammersInDateRange1: [StockDay{ date=2016-10-27, open=58.78, high=58.22, low=33.3, close=58.55, isHammer=true }]

hammersInDateRange2: [StockDay{ date=2016-10-27, open=58.78, high=58.22, low=33.3, close=58.55, isHammer=true }]

hammersInDateRange3: [StockDay{ date=2016-10-27, open=58.78, high=58.22, low=33.3, close=58.55, isHammer=true }]


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154