001    /**
002     * Copyright 2007 Mike Kroutikov.
003     *
004     * This program is free software; you can redistribute it and/or modify
005     *   it under the terms of the Lesser GNU General Public License as 
006     *   published by the Free Software Foundation; either version 3 of
007     *   the License, or (at your option) any later version.
008     *
009     *   This program is distributed in the hope that it will be useful,
010     *   but WITHOUT ANY WARRANTY; without even the implied warranty of
011     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012     *   Lesser GNU General Public License for more details.
013     *
014     *   You should have received a copy of the Lesser GNU General Public License
015     *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
016     */
017    
018    package org.otfeed.protocol.request;
019    
020    import org.otfeed.command.VolumeStyleEnum;
021    import org.otfeed.event.ICompletionDelegate;
022    import org.otfeed.event.IDataDelegate;
023    import org.otfeed.event.OTBBO;
024    import org.otfeed.event.OTMMQuote;
025    import org.otfeed.event.OTQuote;
026    import org.otfeed.event.OTTrade;
027    import org.otfeed.protocol.CommandEnum;
028    import org.otfeed.protocol.DataEnum;
029    import org.otfeed.protocol.ProtocolException;
030    
031    import org.otfeed.protocol.request.quote.QuoteReader;
032    
033    import java.nio.ByteBuffer;
034    
035    import java.util.Date;
036    import java.util.Map;
037    import java.util.HashMap;
038    
039    /**
040     * Request to receive historical quote events.
041     */
042    public final class HistTicksRequest extends AbstractSessionRequest {
043    
044            private final String exchangeCode;
045            private final String symbolCode;
046            private final Date   startDate;
047            private final Date   endDate;
048            private final VolumeStyleEnum volumeStyle;
049            private final int    mask;
050    
051            public String getExchangeCode()  { return exchangeCode; }
052            public String getSymbolCode()    { return symbolCode; }
053            public Date   getStartDate()     { return startDate; }
054            public Date   getEndDate()       { return endDate; }
055            public VolumeStyleEnum getVolumeStyle() { return volumeStyle; }
056    
057            private final Map<Integer,QuoteReader> map = new HashMap<Integer,QuoteReader>();
058    
059            public HistTicksRequest(int requestId, 
060                            String exchangeCode,
061                            String symbolCode,
062                            Date startDate,
063                            Date endDate,
064                            VolumeStyleEnum volumeStyle,
065                            IDataDelegate<OTQuote>   quoteDelegate,
066                            IDataDelegate<OTTrade>   tradeDelegate,
067                            IDataDelegate<OTMMQuote> mmQuoteDelegate,
068                            IDataDelegate<OTBBO>     bboDelegate,
069                            ICompletionDelegate completionDelegate) {
070    
071                    super(CommandEnum.REQUEST_HIST_TICKS, 
072                                    requestId, completionDelegate);
073    
074                    Check.notNull(exchangeCode,  "exchangeCode");
075                    Check.notNull(symbolCode,  "symbolCode");
076                    Check.notNull(startDate, "startDate");
077                    Check.notNull(endDate, "endDate");
078                    Check.notNull(volumeStyle, "volumeStyle");
079    
080                    this.exchangeCode  = exchangeCode;
081                    this.symbolCode    = symbolCode;
082                    this.startDate     = startDate;
083                    this.endDate       = endDate;
084                    this.volumeStyle   = volumeStyle;
085                    
086                    int mask = 0;
087                    if(quoteDelegate != null) {
088                            QuoteReader rdr = QuoteReader.quoteReader(quoteDelegate);
089                            map.put(rdr.type.code, rdr);
090                            mask |= rdr.mask;
091                    }
092                    if(tradeDelegate != null) {
093                            QuoteReader rdr = QuoteReader.tradeReader(tradeDelegate);
094                            map.put(rdr.type.code, rdr);
095                            mask |= rdr.mask;
096                    }
097                    if(mmQuoteDelegate != null) {
098                            QuoteReader rdr = QuoteReader.mmQuoteReader(mmQuoteDelegate);
099                            map.put(rdr.type.code, rdr);
100                            mask |= rdr.mask;
101                    }
102                    if(bboDelegate != null) {
103                            // TODO: bbo events impossible here?
104                            QuoteReader rdr = QuoteReader.bboReader(bboDelegate);
105                            map.put(rdr.type.code, rdr);
106                            mask |= rdr.mask;
107                    }
108                    if(mask == 0) {
109                            throw new IllegalArgumentException("must set at least one quote event delegate");
110                    }
111                    
112                    switch(volumeStyle) {
113                    case INDIVIDUAL:
114                            mask |= VolumeStyleEnum.INDIVIDUAL_VOLUME_FLAG;
115                            break;
116                    case COMPOUND:
117                            break;
118                    default:
119                            throw new IllegalArgumentException("unrecognized volumeStyle: " + volumeStyle);
120                    }
121                    this.mask = mask;
122            }
123    
124            @Override
125            public void writeRequest(ByteBuffer out) {
126                    super.writeRequest(out);
127    
128                    Util.writeString(out, exchangeCode, 15);
129                    Util.writeString(out, symbolCode, 15);
130                    Util.writeDate(out, startDate);
131                    Util.writeDate(out, endDate);
132                    out.putInt(mask);
133            }
134            
135            @Override
136            public JobStatus handleMessage(Header header, ByteBuffer in) {
137    
138                    if(header.getCommand() != CommandEnum.REQUEST_HIST_DATA) {
139                            throw new ProtocolException("unexpected command: "
140                                    + header.getCommand(), in);
141                    }
142    
143                    int count = in.getInt();
144    
145                    while( count -- > 0) {
146    
147                            int typeCode = in.get();
148                            if(typeCode == DataEnum.EOD.code) {
149                                    return JobStatus.FINISHED;
150                            }
151    
152                            QuoteReader reader = map.get(typeCode);
153                            if(reader == null) {
154                                    throw new ProtocolException("unrecognized type: " + typeCode, in);
155                            }
156    
157                            reader.read(header, in);
158                    }
159    
160                    return JobStatus.ACTIVE;
161            }
162    
163            @Override
164            public final CommandEnum getCancelCommand() {
165                    return CommandEnum.CANCEL_HIST_DATA; // fixme??
166            }
167    }