Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: mas_gui/Chart.java


1   /* Copyright 1998 - 2003: Jim Cochrane - see file forum.txt */
2   
3   package mas_gui;
4   
5   import java.awt.*;
6   import java.awt.event.*;
7   import java.util.Vector;
8   import java.util.Hashtable;
9   import java.util.Enumeration;
10  import java.util.Properties;
11  import java.io.*;
12  import graph.*;
13  import support.*;
14  import common.*;
15  
16  class WindowSettings implements Serializable {
17    public WindowSettings(Dimension s, Point l) {
18      size_ = s;
19      location_ = l;
20    }
21  
22    public Point location() { return location_; }
23    public Dimension size() { return size_; }
24  
25    private Point location_;
26    private Dimension size_;
27  }
28  
29  class ChartSettings implements Serializable {
30    public ChartSettings(Dimension sz, Properties printprop, Point loc,
31        Vector upperind, Vector lowerind, boolean replace_ind) {
32      size_ = sz;
33      print_properties_ = printprop;
34      location_ = loc;
35      upper_indicators_ = upperind;
36      lower_indicators_ = lowerind;
37      replace_indicators_ = replace_ind;
38      window_settings = new Hashtable();
39    }
40    public Dimension size() { return size_; }
41    public Point location() { return location_; }
42    public Properties print_properties() { return print_properties_; }
43    public Vector upper_indicators() { return upper_indicators_;}
44    public Vector lower_indicators() { return lower_indicators_;}
45    public boolean replace_indicators() { return replace_indicators_; }
46    public WindowSettings wsettings(String title) {
47      WindowSettings result = (WindowSettings) window_settings.get(title);
48      return result;
49    }
50    public void add_window_setting(WindowSettings ws, String title) {
51      window_settings.put(title, ws);
52    }
53  
54    private Dimension size_;
55    private Properties print_properties_;
56    private Point location_;
57    private Vector upper_indicators_;
58    private Vector lower_indicators_;
59    private boolean replace_indicators_;
60    private Hashtable window_settings;
61  }
62  
63  /** Market analysis GUI chart component */
64  public class Chart extends Frame implements Runnable, NetworkProtocol {
65    public Chart(DataSetBuilder builder, String sfname) {
66      super("Chart");
67      ++window_count;
68      data_builder = builder;
69      this_chart = this;
70      Vector _tradables = null;
71      saved_dialogs = new Vector();
72      _indicators = null;
73  
74      if (sfname != null) serialize_filename = sfname;
75  
76      if (window_count == 1) {
77        ChartSettings settings;
78        try {
79          FileInputStream chartfile =
80            new FileInputStream(serialize_filename);
81          ObjectInputStream ios = new ObjectInputStream(chartfile);
82          settings = (ChartSettings) ios.readObject();
83          window_settings = settings;
84        }
85        catch (IOException e) {
86          // Most likely the file hasn't been created yet - no error.
87        }
88        catch (ClassNotFoundException e) {
89          System.err.println("Class not found!" + e);
90        }
91      }
92      try {
93        if (_tradables == null) {
94          _tradables = data_builder.tradable_list();
95          if (! _tradables.isEmpty()) {
96            // Each tradable has its own period type list; but for now,
97            // just retrieve the list for the first tradable and use
98            // it for all tradables.
99            _period_types = data_builder.trading_period_type_list(
100             (String) _tradables.elementAt(0));
101           if (data_builder.connection.error_occurred()) {
102             abort(data_builder.connection.result().toString(),
103               null);
104           }
105           current_period_type = initial_period_type(_period_types);
106         } else {
107           abort("Server's list of tradables is empty.", null);
108         }
109       }
110     }
111     catch (IOException e) {
112       System.err.println("IO exception occurred: " + e + " - aborting");
113       e.printStackTrace();
114       quit(-1);
115     }
116     String s = null;
117     if (_tradables != null && _tradables.size() > 0) {
118       s = (String) _tradables.elementAt(0);
119     }
120     initialize_GUI_components(s);
121     new Thread(this).start();
122   }
123 
124   // From parent Runnable - for threading
125   public void run() {
126     // null method
127   }
128 
129   // List of all tradables in the server's database
130   public Vector tradables() {
131     Vector result = null;
132     try {
133       result = data_builder.tradable_list();
134     }
135     catch (IOException e) {
136       System.err.println("IO exception occurred, bye ...");
137       quit(-1);
138     }
139     return result;
140   }
141 
142   // indicators
143   public Hashtable indicators() {
144     if (_indicators == null || new_indicators) {
145       new_indicators = false;
146       Vector inds_from_server = data_builder.last_indicator_list();
147       if (previous_open_interest != data_builder.open_interest() ||
148           ! Utilities.lists_match(inds_from_server,
149           old_indicators_from_server)) {
150         // The old indicators are not the same as the indicators
151         // just obtained from the server (or there are not yet
152         // any old indicators), so the indicator lists need to
153         // be rebuilt.
154         old_indicators_from_server = inds_from_server;
155         make_indicator_lists(inds_from_server);
156       }
157       previous_open_interest = data_builder.open_interest();
158     }
159     return _indicators;
160   }
161 
162   private void make_indicator_lists(Vector inds_from_server) {
163     Enumeration ind_iter;
164     String s;
165     int ind_count = inds_from_server.size();
166     Hashtable valid_indicators;
167     if (ind_count > 0) {
168       valid_indicators = new Hashtable(inds_from_server.size());
169     } else {
170       valid_indicators = new Hashtable();
171     }
172     ordered_indicator_list = new Vector();
173     int i;
174     for (i = 0; i < inds_from_server.size(); ++i) {
175       Object o = inds_from_server.elementAt(i);
176       valid_indicators.put(o, new Integer(i + 1));
177     }
178     // User-selected indicators, in order:
179     ind_iter = Configuration.instance().indicator_order().elements();
180     _indicators = new Hashtable();
181     Vector special_indicators = new Vector();
182     special_indicators.addElement(No_lower_indicator);
183     special_indicators.addElement(No_upper_indicator);
184     special_indicators.addElement(Volume);
185     if (data_builder.open_interest()) {
186       special_indicators.addElement(Open_interest);
187     }
188     // Insert into _indicators all user-selected indicators that
189     // are either in the list returned by the server or are one of
190     // the special strings for no upper/lower indicator, volume,
191     // or open interest.
192     while (ind_iter.hasMoreElements()) {
193       s = (String) ind_iter.nextElement();
194       if (valid_indicators.containsKey(s)) {
195         // Add valid indicators (from the server's point of view)
196         // to both `_indicators' and `ordered_indicator_list'.
197         _indicators.put(s, valid_indicators.get(s));
198         ordered_indicator_list.addElement(s);
199       }
200       else {
201         for (i = 0; i < special_indicators.size(); ++i) {
202           if (s.equals(special_indicators.elementAt(i))) {
203             // Add special indicators only to
204             // `ordered_indicator_list'.
205             ordered_indicator_list.addElement(s);
206             special_indicators.removeElement(s);
207             break;
208           }
209         }
210       }
211     }
212     // Insert the special indicators (no-upper indicator, ...) if
213     // they aren't already there.
214     for (i = 0; i < special_indicators.size(); ++i) {
215       s = (String) special_indicators.elementAt(i);
216       if (!_indicators.containsKey(s)) {
217         _indicators.put(s, new Integer(_indicators.size() + 1));
218         ordered_indicator_list.addElement(s);
219       }
220     }
221 
222     // Update current_lower_indicators and current_upper_indicators:
223     // remove any elements that are no longer valid.
224     Vector remove_list = new Vector();
225     for (ind_iter = current_lower_indicators.elements();
226         ind_iter.hasMoreElements(); ) {
227       s = (String) ind_iter.nextElement();
228       if (! ordered_indicator_list.contains(s)) {
229         remove_list.addElement(s);
230       }
231     }
232     for (i = 0; i < remove_list.size(); ++i) {
233       while (current_lower_indicators.lastIndexOf(
234         remove_list.elementAt(i)) != -1) {
235         current_lower_indicators.removeElement(
236           remove_list.elementAt(i));
237       }
238     }
239     remove_list.removeAllElements();
240     for (ind_iter = current_upper_indicators.elements();
241         ind_iter.hasMoreElements(); ) {
242       s = (String) ind_iter.nextElement();
243       if (! ordered_indicator_list.contains(s)) {
244         remove_list.addElement(s);
245       }
246     }
247     for (i = 0; i < remove_list.size(); ++i) {
248       while (current_upper_indicators.lastIndexOf(
249         remove_list.elementAt(i)) != -1) {
250         current_upper_indicators.removeElement(
251           remove_list.elementAt(i));
252       }
253     }
254     if (ma_menu_bar != null) ma_menu_bar.update_indicators();
255   }
256 
257   // Indicators in user-specified order
258   public Vector ordered_indicators() {
259     if (ordered_indicator_list == null) {
260       // Force creation of ordered_indicator_list.
261       indicators();
262     }
263     return ordered_indicator_list;
264   }
265 
266   // Result of last request to the server
267   public int request_result() {
268     return data_builder.request_result();
269   }
270 
271   // Register `d' to save its size and location on exit with its title
272   // as a key.
273   public void register_dialog_for_save_settings(Dialog d) {
274     saved_dialogs.addElement(d);
275   }
276 
277   // Settings for dialog with title `s'
278   public WindowSettings settings_for(String s) {
279     WindowSettings result = null;
280     if (window_settings != null) {
281       result = window_settings.wsettings(s);
282     }
283     return result;
284   }
285 
286   // Take action when notified that period type changed.
287   void notify_period_type_changed(String new_period_type) {
288     if (! current_period_type.equals(new_period_type)) {
289       current_period_type = new_period_type;
290       period_type_change = true;
291       if (current_tradable != null) {
292         request_data(current_tradable);
293       }
294       period_type_change = false;
295     }
296   }
297 
298   // Request data for the specified tradable and display it.
299   void request_data(String tradable) {
300     DataSet dataset, main_dataset;
301     Configuration conf = Configuration.instance();
302     int count;
303     String current_indicator;
304     // Don't redraw the data if it's for the same tradable as before.
305     if (period_type_change || ! tradable.equals(current_tradable)) {
306       GUI_Utilities.busy_cursor(true, this);
307       try {
308         data_builder.send_market_data_request(tradable,
309           current_period_type);
310         if (request_result() == OK) {
311           // Ensure that the indicator list is up-to-date with
312           // respect to `tradable'.
313           data_builder.send_indicator_list_request(tradable,
314             current_period_type);
315           // Force call to `indicators()' to create new indicator
316           // lists with the result of the above request.
317           new_indicators = true;
318           indicators();
319         } else {
320           if (request_result() == Invalid_symbol) {
321             handle_nonexistent_sybmol(tradable);
322           } else if (request_result() == Warning) {
323             new ErrorBox("Warning", "Error occurred retrieving " +
324               "data for " + tradable, this_chart);
325           }
326           GUI_Utilities.busy_cursor(false, this);
327           return;
328         }
329       }
330       catch (Exception e) {
331         fatal("Request to server failed: ", e);
332       }
333       //Ensure that all graph's data sets are removed.  (May need to
334       //change later.)
335       main_pane.clear_main_graph();
336       main_pane.clear_indicator_graph();
337       main_dataset = data_builder.last_market_data();
338       link_with_axis(main_dataset, null);
339       main_pane.add_main_data_set(main_dataset);
340       if (! current_upper_indicators.isEmpty()) {
341         // Retrieve the data for the newly requested tradable for the
342         // upper indicators, add it to the upper graph and draw
343         // the new indicator data and the tradable data.
344         count = current_upper_indicators.size();
345         for (int i = 0; i < count; ++i) {
346           current_indicator = (String)
347             current_upper_indicators.elementAt(i);
348           try {
349             data_builder.send_indicator_data_request(((Integer)
350               indicators().get(current_indicator)).
351                 intValue(), tradable, current_period_type);
352           } catch (Exception e) {
353             fatal("Exception occurred", e);
354           }
355           dataset = data_builder.last_indicator_data();
356           dataset.set_dates_needed(false);
357           dataset.set_color(
358             conf.indicator_color(current_indicator, true));
359           link_with_axis(dataset, current_indicator);
360           main_pane.add_main_data_set(dataset);
361         }
362       }
363       current_tradable = tradable;
364       set_window_title();
365       if (! current_lower_indicators.isEmpty()) {
366         // Retrieve the indicator data for the newly requested
367         // tradable for the lower indicators and draw it.
368         count = current_lower_indicators.size();
369         for (int i = 0; i < count; ++i) {
370           current_indicator = (String)
371             current_lower_indicators.elementAt(i);
372           if (current_lower_indicators.elementAt(i).equals(Volume)) {
373             // (Nothing to retrieve from server)
374             dataset = data_builder.last_volume();
375           } else if (current_lower_indicators.elementAt(i).equals(
376               Open_interest)) {
377             // (Nothing to retrieve from server)
378             dataset = data_builder.last_open_interest();
379           } else {
380             try {
381               data_builder.send_indicator_data_request(((Integer)
382                 indicators().get(current_indicator)).
383                   intValue(), tradable, current_period_type);
384             } catch (Exception e) {
385               fatal("Exception occurred", e);
386             }
387             dataset = data_builder.last_indicator_data();
388           }
389           if (dataset != null) {
390             dataset.set_color(
391               conf.indicator_color(current_indicator, false));
392             link_with_axis(dataset, current_indicator);
393             add_indicator_lines(dataset, current_indicator);
394             main_pane.add_indicator_data_set(dataset);
395           }
396         }
397       }
398       main_pane.repaint_graphs();
399       GUI_Utilities.busy_cursor(false, this);
400     }
401   }
402 
403 // Implementation
404 
405   // Add any extra lines to the indicator graph - specified in the
406   // configuration.
407   protected void add_indicator_lines(DataSet dataset, String indicator) {
408     if (current_lower_indicators.isEmpty()) {
409       return;
410     }
411 
412     Vector lines;
413     double d1, d2;
414     Configuration conf = Configuration.instance();
415     lines = conf.vertical_indicator_lines_at(indicator);
416     if (lines != null && lines.size() > 0) {
417       for (int j = 0; j < lines.size(); j += 2) {
418         d1 = ((Float) lines.elementAt(j)).floatValue();
419         d2 = ((Float) lines.elementAt(j+1)).floatValue();
420         dataset.add_vline(new DoublePair(d1, d2));
421       }
422     }
423     lines = conf.horizontal_indicator_lines_at(indicator);
424     if (lines != null && lines.size() > 0) {
425       for (int j = 0; j < lines.size(); j += 2) {
426         d1 = ((Float) lines.elementAt(j)).floatValue();
427         d2 = ((Float) lines.elementAt(j+1)).floatValue();
428         dataset.add_hline(new DoublePair(d1, d2));
429       }
430     }
431   }
432 
433   // Initialize components and obtain and display data for `symbol' if
434   // it is not null, etc.
435   private void initialize_GUI_components(String symbol) {
436     // Create the main scroll pane, size it, and center it.
437     main_pane = new MA_ScrollPane(_period_types,
438       MA_ScrollPane.SCROLLBARS_NEVER, this, window_settings != null?
439         window_settings.print_properties(): null);
440     if (window_settings != null && window_count == 1) {
441       main_pane.setSize(window_settings.size().width,
442         window_settings.size().height + 2);
443       setLocation(window_settings.location());
444       current_upper_indicators = window_settings.upper_indicators();
445       current_lower_indicators = window_settings.lower_indicators();
446       replace_indicators = window_settings.replace_indicators();
447     } else {
448       main_pane.setSize(800, 460);
449       current_upper_indicators = new Vector();
450       current_lower_indicators = new Vector();
451     }
452     add(main_pane, "Center");
453     if (symbol != null) {
454       if (data_builder.options().print_on_startup() &&
455           window_count == 1) {
456         print_all_charts();
457       }
458       // Show the graph of the first symbol in the selection list.
459       request_data(symbol);
460     }
461     market_selections = new MarketSelection(this);
462     ma_menu_bar = new MA_MenuBar(this, data_builder, _period_types);
463     setMenuBar(ma_menu_bar);
464 
465     // Event listener for close requests
466     addWindowListener(new WindowAdapter() {
467     public void windowClosing(WindowEvent e) { close(); }
468     });
469 
470     pack();
471     show();
472   }
473 
474   // Set the window title using current_tradable and current_lower_indicators.
475   protected void set_window_title() {
476     if (! current_lower_indicators.isEmpty()) {
477       StringBuffer newtitle = new
478         StringBuffer (current_tradable.toUpperCase() + " - ");
479       int i;
480       for (i = 0; i < current_lower_indicators.size() - 1; ++i) {
481         newtitle.append(current_lower_indicators.elementAt(i));
482         newtitle.append(", ");
483       }
484       newtitle.append(current_lower_indicators.elementAt(i));
485       setTitle(newtitle.toString());
486     }
487     else {
488       setTitle(current_tradable.toUpperCase());
489     }
490   }
491 
492   // Notify the user that the symbol chosen does not exist and then
493   // remove the symbol from the selection list.
494   private void handle_nonexistent_sybmol(String symbol) {
495     ErrorBox errorbox = new ErrorBox("",
496       "Symbol " + symbol + " is not in the database.", this_chart);
497     market_selections.remove_selection(symbol);
498   }
499 
500   // Save persistent settings as a serialized file.
501   // Precondition: main_pane != null
502   protected void save_settings() {
503     if (serialize_filename != null) {
504     try {
505       FileOutputStream chartfile =
506         new FileOutputStream(serialize_filename);
507       ObjectOutputStream oos = new ObjectOutputStream(chartfile);
508       ChartSettings cs = new ChartSettings(main_pane.getSize(),
509         main_pane.print_properties, getLocation(),
510         current_upper_indicators, current_lower_indicators,
511         replace_indicators);
512       for (int i = 0; i < saved_dialogs.size(); ++i) {
513         Dialog d = (Dialog) saved_dialogs.elementAt(i);
514         WindowSettings ws = new WindowSettings(
515           d.getSize(), d.getLocation());
516         cs.add_window_setting(ws, d.getTitle());
517       }
518       oos.writeObject(cs);
519       oos.flush();
520       oos.close();
521     }
522     catch (IOException e) {
523       System.err.println("Could not save file " + serialize_filename);
524       System.err.println(e);
525     }
526     }
527   }
528 
529   // Set replace_indicators to its opposite state.
530   protected void toggle_indicator_replacement() {
531     replace_indicators = ! replace_indicators;
532   }
533 
534   // Add a menu item for each indicator to `imenu'.
535   protected void add_indicators(Menu imenu) {
536     MenuItem menu_item;
537     IndicatorListener listener = new IndicatorListener(this);
538     Enumeration ind_keys = ordered_indicator_list.elements();
539     for ( ; ind_keys.hasMoreElements(); ) {
540       menu_item = new MenuItem((String) ind_keys.nextElement());
541       imenu.add(menu_item);
542       menu_item.addActionListener(listener);
543     }
544   }
545 
546   // Print the chart for each member of market_selections
547   protected void print_all_charts() {
548     main_pane.print(true);
549   }
550 
551   /** Close a window.  If this is the last open window, just quit. */
552   protected void close() {
553     if (window_count == 1) {  // Close last remaining window, exit.
554       save_settings();
555       data_builder.logout(true, 0);
556     }
557     else {    // More than 1 windows remain, close this one.
558       --window_count;
559       data_builder.logout(false, 0);
560       dispose();
561     }
562   }
563 
564   /** Quit gracefully, sending a logout request for each open window. */
565   protected void quit(int status) {
566     if (main_pane != null) save_settings();
567     log_out_and_exit(status);
568   }
569 
570   /** Log out of all sessions and exit. */
571   protected void log_out_and_exit(int status) {
572     // Log out the corresponding session for all but one window.
573     for (int i = 0; i < window_count - 1; ++i) {
574       data_builder.logout(false, 0);
575     }
576     // Log out the remaining window and exit with `status'.
577     data_builder.logout(true, status);
578   }
579 
580   // Print fatal error and exit after saving settings.
581   protected void fatal(String s, Exception e) {
582     System.err.println("Fatal error: " + s);
583     if (current_tradable != null) {
584       System.err.println("Current symbol is " + current_tradable);
585     }
586     if (e != null) {
587       System.err.println("(" + e + ")");
588       e.printStackTrace();
589     }
590     System.err.println("Exiting ...");
591     quit(-1);
592   }
593 
594   // Same as `fatal' except `save_settings' is not called.
595   protected void abort(String s, Exception e) {
596     System.err.println("Fatal error: " + s);
597     if (current_tradable != null) {
598       System.err.println("Current symbol is " + current_tradable);
599     }
600     if (e != null) {
601       System.err.println("(" + e + ")");
602       e.printStackTrace();
603     }
604     System.err.println("Exiting ...");
605     log_out_and_exit(-1);
606   }
607 
608   // Link `d' with the appropriate indicator group, using `indicator_name'
609   // as a key.  If `indicator_name' is null, the group for the main
610   // (upper) graph will be used.  If `indicator_name' specifies an
611   // indicator that is not a group member, no action is taken.
612   protected void link_with_axis(DataSet d, String indicator_name) {
613     if (indicator_groups == null) {
614       indicator_groups = (IndicatorGroups)
615         Configuration.instance().indicator_groups().clone();
616     }
617     IndicatorGroup group;
618     if (indicator_name == null) {
619       indicator_name = indicator_groups.Maingroup;
620     }
621     group = indicator_groups.at(indicator_name);
622     if (group != null) {
623       group.attach_data_set(d);
624     }
625   }
626 
627   private boolean vector_has(Vector v, String s) {
628     return Utilities.vector_has(v, s);
629   }
630 
631   // First period type to be displayed - daily, if it exists
632   String initial_period_type(Vector types) {
633     String result = null;
634     String daily = MA_MenuBar.daily_period.toLowerCase();
635     for (int i = 0; result == null && i < types.size(); ++i) {
636       if (((String) types.elementAt(i)).toLowerCase().equals(daily)) {
637         result = (String) types.elementAt(i);
638       }
639     }
640     if (result == null) result = (String) types.elementAt(0);
641 
642     return result;
643   }
644 
645   protected DataSetBuilder data_builder;
646 
647   private Chart this_chart;
648 
649   // # of open windows - so program can exit when last one is closed
650   protected static int window_count = 0;
651 
652   // Main window pane
653   MA_ScrollPane main_pane;
654 
655   // The menu bar for this chart
656   MA_MenuBar ma_menu_bar;
657 
658   // Valid trading period types - static for now, since it is currently
659   // hard-coded in the server
660   protected static Vector _period_types;  // Vector of String
661 
662   // Table of market indicators
663   protected static Hashtable _indicators;  // key: String, value: Integer
664 
665   // Has data_builder.send_indicator_list_request been called since the
666   // last call to `indicators'?
667   protected boolean new_indicators = true;
668 
669   // Indicators in user-specified order - includes no-upper/lower,
670   // volume, and open-interest indicators
671   private static Vector ordered_indicator_list;  // Vector of String
672 
673   // Current selected tradable
674   protected String current_tradable;
675 
676   // Current selected period type
677   protected String current_period_type;
678 
679   // Has the period type just been changed?
680   protected boolean period_type_change;
681 
682   // Upper indicators currently selected for display
683   protected Vector current_upper_indicators;
684 
685   // Lower indicators currently selected for display
686   protected Vector current_lower_indicators;
687 
688   // Should new indicator selections replace, rather than be added to,
689   // existing indicators?
690   boolean replace_indicators;
691 
692   protected MarketSelection market_selections;
693 
694   protected final String No_upper_indicator = "No upper indicator";
695 
696   protected final String No_lower_indicator = "No lower indicator";
697 
698   protected final String Volume = "Volume";
699 
700   protected final String Open_interest = "Open interest";
701 
702   static String serialize_filename;
703 
704   static ChartSettings window_settings;
705 
706   IndicatorGroups indicator_groups;
707 
708   // Dialog windows registered to have their settings saved on exit
709   private Vector saved_dialogs;
710 
711   // Saved result of data_builder.last_indicator_list(), used by
712   // `indicators' to compare old list with new list
713   private Vector old_indicators_from_server = null;
714 
715   // Did the previously retrieved data from the server contain an
716   // open interest field?
717   private boolean previous_open_interest;
718 }