[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GT] SVN Commit r562 - trunk/GT/DB



Author: ras
Date: 2008-03-11 07:36:12 +0100 (Tue, 11 Mar 2008)
New Revision: 562

Added:
   trunk/GT/DB/CSV.pm
Modified:
   trunk/GT/DB/MetaStock.pm
   trunk/GT/DB/MetaStockReader.pm
Log:
CVS.pm is another text based database interface
MetaStock and MetaStockReader pod change only making it clear
that one or the other, but not both are used with preference
given to MetaStockReader


Added: trunk/GT/DB/CSV.pm
===================================================================
--- trunk/GT/DB/CSV.pm	                        (rev 0)
+++ trunk/GT/DB/CSV.pm	2008-03-11 06:36:12 UTC (rev 562)
@@ -0,0 +1,723 @@
+package GT::DB::CSV;
+
+# Copyright 2003 Oliver Bossert
+# This file is distributed under the terms of the General Public License
+# version 2 or (at your option) any later version.
+
+use strict;
+use vars qw(@ISA);
+
+use GT::DB;
+use GT::Prices;
+use GT::Conf;
+use GT::DateTime;
+use Time::Local;
+
+use DBI;
+
+
AT
ISA = qw(GT::DB);
+
+=head1 NAME
+
+GT::DB::CSV - Access to a text files by DBI::CSV
+
+=head1 DESCRIPTION
+
+This module handels the access to textfiles by using the
+DBI:File-module.
+
+=head2 Configuration
+
+You can put some configuration items in ~/.gt/options to indicate where
+the database is.
+
+=over 
+
+=item DB::csv::database : the type of the database ("CSV" by default)
+
+=item DB::csv::dbname : the name of the database ("cours" by default)
+
+=item DB::csv::dbhost : the host of the database ("" = localhost by default)
+
+=item DB::csv::dbuser : the user account on the database
+
+=item DB::csv::dbpasswd : the password of the user account
+
+=back
+
+=head2 Functions
+
+=over
+
+=item C<< GT::DB::csv->new() >>
+
+Creates a new database-object
+
+=cut
+
+sub new {
+    my $type = shift;
+    my $class = ref($type) || $type;
+
+    GT::Conf::default("DB::csv::database", "CSV");
+    GT::Conf::default("DB::csv::dbname", "/home/olf/ablage/private/finalize/GT/database"); 
+    GT::Conf::default("DB::csv::dbhost", "");   #aka localhost
+    GT::Conf::default("DB::csv::dbuser", "");   #aka current user
+    GT::Conf::default("DB::csv::dbpasswd", ""); #aka user is already identified
+
+    my $self = { 'database' => GT::Conf::get("DB::csv::database"),
+		 'dbname'   => GT::Conf::get("DB::csv::dbname"),
+    		 'dbhost'   => GT::Conf::get("DB::csv::dbhost"), 
+		 'dbuser'   => GT::Conf::get("DB::csv::dbuser"), 
+		 'dbpasswd' => GT::Conf::get("DB::csv::dbpasswd"), 
+		 @_
+		};
+
+    if ( $self->{'database'} eq "CSV" ) {
+      GT::Conf::default("DB::csv::connectstring", "DBI:CSV:f_dir=" . $self->{'dbname'} . ";csv_sep_char=\t" );
+    } else {
+      my $addstring = "";
+      if ($self->{'dbhost'}) {
+	$addstring .= ";host=" . $self->{'dbhost'};
+      }
+      GT::Conf::default("DB::csv::connectstring", "DBI:" . $self->{'database'} . 
+			":database=" . $self->{'dbname'} . $addstring );
+    }
+		
+    my $connect_string = GT::Conf::get("DB::csv::connectstring");
+    $self->{'_dbh'} = DBI->connect($connect_string, $self->{'dbuser'},
+    		$self->{'dbpasswd'}) or die "Couldn't connect to database !\n";
+
+    return bless $self, $class;
+}
+
+=item C<< $db->disconnect >>
+
+Disconnects from the database.
+
+=cut
+sub disconnect {
+    my $self = shift;
+    $self->{'_dbh'}->disconnect;
+    delete $self->{'prices'};
+    delete $self->{'dates'};
+}
+
+
+=item C<< $db->init_table($code) >>
+
+Creates the table of stock $code.
+
+=cut
+sub init_table {
+    my ($self, $code) = @_;
+
+    # If we use a CVS-Database, create the directory
+    if ( $self->{'database'} eq "CSV" ) {
+        my $DB_DIR = $self->{'dbname'};
+        if (! -d $DB_DIR) {
+	    mkdir($DB_DIR, 0755) || 
+		die "Could not create directory $DB_DIR.";
+        }
+    }
+
+    $self->{'_dbh'}->do("
+    CREATE TABLE PRICES_$code (
+        open   REAL,
+        close  REAL,
+        high   REAL,
+        low    REAL,
+        volume REAL,
+        date   CHAR(10)
+    )") or die "Could not create table PRICES_$code.";
+}
+
+=item C<< $db->init_add_info() >>
+
+Creates the addinfo-table.
+
+=cut
+sub init_add_info {
+    my $self = shift;
+
+    # If we use a CVS-Database, create the directory
+    if ( $self->{'database'} eq "CSV" ) {
+        my $DB_DIR = $self->{'dbname'};
+        if (! -d $DB_DIR) {
+	    mkdir($DB_DIR, 0755) || 
+		die "Could not create directory $DB_DIR.";
+        }
+    }
+
+    $self->{'_dbh'}->do(<<'EOT') or die "Could not create table addinfo.";
+    CREATE TABLE addinfo (
+        info   CHAR(60),
+        code   CHAR(15),
+        date   CHAR(10),
+        value  CHAR(100)
+    )
+EOT
+}
+
+=item C<< $db->init_add_info() >>
+
+Creates the shares-table.
+
+=cut
+sub init_shares {
+    my $self = shift;
+
+    # If we use a CVS-Database, create the directory
+    if ( $self->{'database'} eq "CSV" ) {
+        my $DB_DIR = $self->{'dbname'};
+        if (! -d $DB_DIR) {
+	    mkdir($DB_DIR, 0755) || 
+		die "Could not create directory $DB_DIR.";
+        }
+    }
+
+    $self->{'_dbh'}->do(<<'EOT') or die "Could not create table addinfo.";
+    CREATE TABLE shares (
+        name   CHAR(100),
+        code   CHAR(15)
+    )
+EOT
+}
+
+=item C<< $db->get_prices($code) >>
+
+Returns a GT::Prices object containing all known prices for the symbol $code.
+
+=cut
+sub get_prices {
+    return get_last_prices(@_, -1);
+}
+
+=item C<< $db->get_last_prices($code, $limit) >>
+
+Returns a GT::Prices object containing the $limit last known prices for
+the symbol $code.
+
+=cut
+sub get_last_prices {
+    my ($self, $code, $limit) = @_;
+
+    my $q = GT::Prices->new($limit);
+    $q->set_timeframe($DAY);
+
+    my $sql = qq{ SELECT open, high, low, close, volume, date
+    		  FROM PRICES_$code ORDER BY date DESC };
+    if ($limit > 0) {
+	$sql .= "LIMIT $limit";
+    }
+
+    my $ref = $self->{'_dbh'}->selectall_arrayref( $sql )
+      or die $self->{'_dbh'}->errstr();
+
+    my @prices = @{$ref};
+    @prices = sort { $b->[5] cmp $a->[5] } @prices;
+
+    my $res;
+    foreach $res (reverse @prices)
+    {
+	$q->add_prices($res);
+    }
+    return $q;
+}
+
+
+# Now some basic management functions
+# ###################################
+
+=item C<< $db->insert($code) >>
+
+Creates the table of stock $code.
+
+=cut
+sub insert {
+    my $self = shift;
+    my %values = @_;
+    no strict "refs";
+
+    # Exit if the Primary Key is not defined
+    my @INDEX_FIELDS = qw/date/;
+    foreach my $f (@INDEX_FIELDS) {
+	return if ( !defined($values{$f}) );
+    }
+
+    my @names = ();
+    my @vals  = ();
+    while ( my ($key, $value) = each %values) {
+	if ($key ne 'code') {
+	    push @names, $key;
+	    push @vals, $value;
+	}
+    }
+
+    for (my $i=0; $i<=$#vals; $i++) {
+	return if ( $vals[$i] !~ /^[\d\.-]+$/ ); # Somtimes a "n/a" occurs
+	$vals[$i] = "'" . $vals[$i] . "'";
+    }
+
+    $self->{'_dbh'}->do( "INSERT INTO PRICES_" .$values{'code'} . " (" . join(", ", @names) . 
+	      ") VALUES (" . join(", ", @vals) . ")" ) 
+	or warn $self->{'_dbh'}->errstr();
+
+}
+
+=item C<< $db->get( parameters ) >>
+
+Get the datasets where all the parameters match
+
+=cut
+sub get {
+    my $self = shift;
+    no strict "refs";
+    my %values = @_;
+
+    my @wheres = ();
+    while ( my ($key, $value) = each %values) {
+	push @wheres, "$key = '$value'" if ($key ne 'code');
+    }
+    my $where = join(" AND ", @wheres);
+    $where = "WHERE " . $where if ( $where ne "" );
+
+    my $ref = $self->{'_dbh'}->selectall_arrayref( "SELECT open, high, low, close, volume, date FROM PRICES_" .$values{'code'} . " " . $where );
+#	or die $self->{'_dbh'}->errstr();
+
+    return @{$ref};
+}
+
+=item C<< $db->available( $code, $date ) >>
+
+Returns 1 if a dataset for the corresponding day is available.
+
+=cut
+sub available {
+    my ($self, $code, $date) = @_;
+    my @data = $self->get( "date" => $date, "code" => $code );
+    my $res = ( $#data >= 0 ) ? 1 : 0;
+    return $res;
+}
+
+=item C<< $db->get( parameters ) >>
+
+Delete the datasets where all the parameters match
+
+=cut
+sub del {
+    my $self = shift;
+    my %values = @_;
+    no strict "refs";
+
+    my @wheres = ();
+    while ( my ($key, $value) = each %values) {
+	push @wheres, "$key = '$value'" if ($key ne 'code');
+    }
+    my $where = join(" AND ", @wheres);
+    $where = "WHERE " . $where if ( $where ne "" );
+
+    $self->{'_dbh'}->do( "DELETE FROM PRICES_" .$values{'code'} . " " . $where )
+	or die $self->{'_dbh'}->errstr();
+
+}
+
+=item C<< $db->edit( parameters ) >>
+
+Edit the dataset where the date and the code matches
+
+=cut
+sub edit {
+    my $self = shift;
+    my %values = @_;
+    no strict "refs";
+    my @INDEX_FIELDS = qw/date/;
+
+    # Define what field to update
+    my @wheres = ();
+    foreach my $f (@INDEX_FIELDS) {
+	if ( defined($values{$f}) ) {
+	    push @wheres, "$f = '" . $values{$f} . "'";
+	    delete($values{$f});
+	}
+    }
+    my $where = join(" AND ", @wheres);
+    $where = "WHERE " . $where if ( $where ne "" );
+
+    return if ( $where eq "" );
+
+    # What is to be updated?
+    my @sets = ();
+    while ( my ($key, $value) = each %values) {
+	push @sets, "$key = '$value' " if ($key ne 'code');
+    }
+    my $set = "SET " . join(", ", @sets);
+
+    my $ref = $self->{'_dbh'}->do( "UPDATE PRICES_" .$values{'code'} . " " . $set . $where )
+	or die $self->{'_dbh'}->errstr();
+
+}
+
+=item C<< $db->table_exists($code) >>
+
+Test if a table for stock $code already exists
+
+=cut
+sub table_exists {
+  my $self = shift;
+  my $code = shift;
+  my $test = $self->get( "code" => $code );
+  my $ret = 0;
+  $ret = 1 if ( defined($test) );
+  return ( $ret );
+}
+
+
+# Management of shares (code, name)
+# #################################
+
+=item C<< $db->get_db_name($code) >>
+
+Returns the name of the stock designated by $code.
+
+=cut
+sub get_db_name {
+    my ($self, $code) = @_;
+    my $sql = "SELECT name FROM shares WHERE code = '$code'";
+    my $sth = $self->{'_dbh'}->prepare($sql) || die $self->{'_dbh'}->errstr;
+    $sth->execute(); # || warn $self->{'_dbh'}->errstr;
+    my $res = $sth->fetchrow_arrayref;
+    $res->[0] =~ s/^\s*//s;
+    $res->[0] =~ s/\s*$//s;
+    return $res->[0];
+}
+
+=item C<< $db->get_db_code($name) >>
+
+Returns the code of the stock designated by $name.
+
+=cut
+sub get_db_code {
+    my ($self, $name) = @_;
+    my $sql = "SELECT code, name FROM shares";
+    my $ref = $self->{'_dbh'}->selectall_arrayref( $sql )
+      or die $self->{'_dbh'}->errstr();
+    my @codes = @{$ref};
+    my $res = $name;
+    foreach my $code (@codes)
+    {
+        $res = $code->[0] if ($code->[1] =~ /$name/i);
+    }
+    $res =~ s/^\s*//s;
+    $res =~ s/\s*$//s;
+    return $res;
+}
+
+
+# Management of additional Informations
+# #####################################
+
+=item C<< $db->get_add_info($code,$date) >>
+
+Returns an additional information about the stock
+
+=cut
+sub get_add_info {
+    my ($self, $info, $code, $date) = @_;
+
+    my $sql = "SELECT value FROM addinfo WHERE info = '$info' AND code = '$code'";
+    $sql .= " AND date = '$date'" if (defined($date));
+
+    my $sth = $self->{'_dbh'}->prepare($sql) || die $self->{'_dbh'}->errstr;
+    $sth->execute() || die $self->{'_dbh'}->errstr;
+    my $res = $sth->fetchrow_arrayref;
+    $res->[0] =~ s/^\s*//;
+    $res->[0] =~ s/\s*$//;
+    return $res->[0];
+}
+
+=item C<< $db->get_add_info($code,$date) >>
+
+Returns an additional information about the stock
+
+=cut
+sub set_db_name {
+    my ($self, $code, $name) = @_;
+
+    # Check if the dataset is available
+    my $res = $self->get_db_name($code);
+
+    if ( defined($res) ) {
+	my $sql = "UPDATE shares SET name = '$name' WHERE code = '$code'";
+	my $sth = $self->{'_dbh'}->do($sql) || die $self->{'_dbh'}->errstr;
+    } else {
+	my $sql = "INSERT INTO shares (code, name) VALUES ('$code', '$name')";
+	my $sth = $self->{'_dbh'}->do($sql) || die $self->{'_dbh'}->errstr;
+    }
+}
+
+
+=item C<< $db->set_add_info($value, $info, $code, $date) >>
+
+Set an additional information about the stock
+
+=cut
+sub set_add_info {
+    my ($self, $value, $info, $code, $date) = @_;
+
+    # Check if the dataset is available
+    my $res = $self->get_add_info($info, $code, $date);
+
+    if ( defined($res) ) {
+	my $sql = "UPDATE addinfo SET value = '$value' WHERE info = '$info' AND code = '$code'";
+	$sql .= " AND date = '$date'" if (defined($date));
+	my $sth = $self->{'_dbh'}->do($sql) || die $self->{'_dbh'}->errstr;
+    } else {
+	$date = "-" if ( !defined($date) );
+	my $sql = "INSERT INTO addinfo (info, code, date, value) VALUES ('$info', '$code', '$date', '$value')";
+	my $sth = $self->{'_dbh'}->do($sql) || die $self->{'_dbh'}->errstr;
+    }
+
+}
+
+
+# Communication with other databases/sources
+# ##########################################
+
+=item C<< $db->update_from_source($code) >>
+
+This function is getting the actual information from the web.
+
+=cut
+sub update_from_source {
+    my $self = shift;
+    my ($source, $code) = @_;
+
+    my $newtable = 0;
+    if ( !$self->table_exists( $code ) ) {
+      $self->init_table( $code );
+      $newtable = 1;
+    }
+
+    my ($sec, $min, $hour, $d, $m, $y, $wd, $yd) = localtime;
+    my $today = sprintf("%04d-%02d-%02d", $y + 1900, $m + 1, $d);
+
+    # Return if the current day is already available
+    return if ( $self->available($code, $today) == 1 );
+
+    # Check for the update-cycle
+    GT::Conf::default("DB::Source::$source::UpdateCycle", "12"); # 12h
+    my $update = GT::Conf::get("DB::Source::$source::UpdateCycle");
+    my $lastupdate = $self->get_add_info( "Update_$source", $code );
+    my $now = timelocal($sec, $min, $hour, $d, $m, $y);
+    $update = $update * 60 * 60; # --> seconds
+
+    print ">>" . $lastupdate . "<<\n";
+
+    if ( $lastupdate eq "" || ( $lastupdate + $update < $now  ) ) {
+	my $prices = $self->get_last_prices($code, 1);
+	my $latest_date = $prices->find_nearest_date($today);
+
+#	return if ( !defined($prices->at_date($latest_date)) );
+	my $last_date;
+	if ( !defined($prices->at_date($latest_date)) ) {
+	  $last_date = "1960-01-01";
+	} else {
+	  $last_date = $prices->at_date($latest_date)->[$DATE];
+	}
+	# Decrement the month
+	my @last_dat = split /-/, $last_date;
+	#$last_dat[1]--;
+	$last_date = join("-", @last_dat);
+	print $last_date . " to " . $today . "\n";
+	my @data = ();
+	my $getstring = "use GT::DB::$source; my \$s = GT::DB::$source->new();
+                         \
AT
data = \$s->get_price_interval('$code', '$last_date', '$today');";
+
+	eval $getstring;
+	if ($@) {
+	    warn "$@ : $getstring";
+	    return;
+	}
+	
+	for (my $d=0; $d <= $#data; $d++) {
+
+	  if ( $self->available($code, $data[$d][5]) == 1 ) {
+	    $self->edit( code   => $code, 
+			 date   => $data[$d][5], 
+			 open   => $data[$d][0],
+			 high   => $data[$d][1],
+			 low    => $data[$d][2],
+			 close  => $data[$d][3],
+			 volume => $data[$d][4]
+		       );
+	  } else {
+	    $self->insert( code   => $code, 
+			   date   => $data[$d][5], 
+			   open   => $data[$d][0],
+			   high   => $data[$d][1],
+			   low    => $data[$d][2],
+			   close  => $data[$d][3],
+			   volume => $data[$d][4]
+			 );
+	  }
+	}
+
+    }
+
+    # New update-information
+    $self->set_add_info("$now" ,"Update_$source", $code);
+
+}
+
+=item C<< $db->get_all_prices($code) >>
+
+Dummy function. Need to define a clear interface for the exchange.
+
+=cut
+sub get_all_prices {
+  my $self = shift;
+  my $code = shift;
+  return ( $self->get( code => $code) );
+}
+
+=item C<< $db->merge_from_source($source, $code) >>
+
+Merges the content of an other database/source into the current db. 
+This needs to be updated with a "ranking" algorithm. 
+
+=cut
+sub merge_from_source {
+  my $self = shift;
+  my ($source, $code) = @_;
+
+  # Create the table ?!?!
+  $self->init_table( $code ) if ($self->table_exists("$code") == 0); 
+
+  my @data = ();
+  my $getstring = "use GT::DB::$source; my \$s = GT::DB::$source->new();
+                     \
AT
data = \$s->get_all_prices('$code');\$s->disconnect();";
+
+  eval $getstring;
+  if ($@) {
+    warn "$@ : $getstring";
+    return;
+  }
+
+  for (my $d=0; $d <= $#data; $d++) {
+
+    my ($open, $high, $low, $close, $volume, $date) = ($data[$d][0], $data[$d][1], $data[$d][2], $data[$d][3], $data[$d][4], $data[$d][5]);
+
+    #print $date . "\n";
+    #print "NEW: " . join( "\t", ($open, $high, $low, $close, $volume, $date) ) . "\n";
+
+    my @data2 = ( [] );
+    if ( $self->available($code, $date) == 1 ) {
+
+      @data2 = $self->get( code => $code, date => $date );
+      #print  "OLD: " . join( "\t", @{$data2[0]} ) . "\n";
+
+      if ( $open != $data2[0][0] ) {
+	if ( $open - $data2[0][0] <= 0.01 ) {
+	  $open = $data2[0][0];
+	} else {
+	  print STDERR "  $code $date differs : OPEN   $open != " . $data2[0][0] . "\n" 
+	}
+      }
+      if ( $high != $data2[0][1] ) {
+	if ( $high - $data2[0][1] <= 0.01 ) {
+	  $high = $data2[0][1];
+	} else {
+	  print STDERR "  $code $date differs : HIGH   $high != " . $data2[0][1] . "\n" 
+	}
+      }
+      if ( $low != $data2[0][2] ) {
+	if ( $low - $data2[0][2] <= 0.01 ) {
+	  $low = $data2[0][2];
+	} else {
+	  print STDERR "  $code $date differs : LOW   $low != " . $data2[0][2] . "\n" 
+	}
+      }
+      if ( $close != $data2[0][3] ) {
+	if ( $close - $data2[0][3] <= 0.01 ) {
+	  $close = $data2[0][3];
+	} else {
+	  print STDERR "  $code $date differs : CLOSE   $close != " . $data2[0][3] . "\n" 
+	}
+      }
+      if ( $volume != $data2[0][4] ) {
+	if ( $volume == 0 ) {
+	  $volume = $data2[0][4];
+	} elsif ( $data2[0][4] == 0 ) {
+	} else {
+	  print STDERR "  $code $date differs : VOLUME   $volume != " . $data2[0][4] . "\n" unless (abs($volume != $data2[0][4]) <= 1);
+	}
+      }
+
+      $self->edit( code   => $code,
+		   date   => $date,
+		   open   => $open,
+		   high   => $high,
+		   low    => $low,
+		   close  => $close,
+		   volume => $volume
+		 );
+
+    } else {
+      $self->insert( code   => $code,
+		     date   => $date,
+		     open   => $open,
+		     high   => $high,
+		     low    => $low,
+		     close  => $close,
+		     volume => $volume
+		   );
+
+    }
+
+    ##print "x" x 60 . "\n";
+
+  }
+
+}
+
+=item C<< $db->merge_all_from_source($source) >>
+
+Merges the content of all shares in an other database/source into the current db. 
+
+=cut
+sub merge_all_from_source {
+  my $self = shift;
+  my ($source) = @_;
+  my $ref = $self->{'_dbh'}->selectall_arrayref( "SELECT code FROM shares" );
+  my @codes = @{$ref};
+  for (my $i=0; $i<=$#codes; $i++) {
+      $codes[$i][0] =~ s/ *$//;
+      print $codes[$i][0] . "\n";
+      $self->merge_from_source($source, $codes[$i][0]);
+  }
+}
+
+=item C<< $db->update_all_from_source($source) >>
+
+Updates all shares from a source. 
+
+=cut
+sub update_all_from_source {
+  my $self = shift;
+  my ($source) = @_;
+  my $ref = $self->{'_dbh'}->selectall_arrayref( "SELECT code FROM shares" );
+  my @codes = @{$ref};
+  for (my $i=0; $i<=$#codes; $i++) {
+      $codes[$i][0] =~ s/ *$//;
+      print $codes[$i][0] . "\n";
+      $self->update_from_source($source, $codes[$i][0]);
+  }
+}
+
+=pod
+
+=back
+
+=cut
+1;

Modified: trunk/GT/DB/MetaStock.pm
===================================================================
--- trunk/GT/DB/MetaStock.pm	2008-03-11 06:26:06 UTC (rev 561)
+++ trunk/GT/DB/MetaStock.pm	2008-03-11 06:36:12 UTC (rev 562)
@@ -15,17 +15,32 @@
 
 =head2 Overview
 
-The MetaStock access module is able to retrieve quotes from almost any type of MetaStock/Computrac database.
+The MetaStock access module is able to retrieve quotes from almost any
+type of MetaStock/Computrac database.
 
 =head2 Note
 
-This module simply call the software "MetaStockReader" to get quotes, with a directory and a symbol as main parameters. Please refer to it's source if you want to learn more about it.
+This module calls the binary program identified by the $HOME/.gt/options
+file option "DB::metastock::program" ("/bourse/tools/MetaStockReader" by default)
+to get quotes from your metastock database.
 
+$HOME/.gt/options file option
+"DB::metastock::directory" must indicate the directory of the metastock database.
+(no default).
+
+Please refer to /bourse/tools/MetaStockReader source if you want to learn
+more about it.
+
+NOTE: this module and the companion binary program has been depreciated
+in favor of the stand-alone perl module (unfortunately) also named
+MetaStockReader. refer to that modules pod
+
 =head2 Configuration
 
 You can indicate the directory which contains the MetaStock database
 by setting the DB::metastock::directory configuration item. You can
-also set DB::metastock::program to indicate where the MetaStockReader.
+also set DB::metastock::program to indicate where the MetaStockReader
+binary program is located (complete pathname).
 
 =head2 new()
 

Modified: trunk/GT/DB/MetaStockReader.pm
===================================================================
--- trunk/GT/DB/MetaStockReader.pm	2008-03-11 06:26:06 UTC (rev 561)
+++ trunk/GT/DB/MetaStockReader.pm	2008-03-11 06:36:12 UTC (rev 562)
@@ -15,6 +15,7 @@
 #		: test if security already exists before putting in the table.
 # v1.6 04/06/2005 : Changed the DB object interface to allow better timeframe support (Jo�Costa)
 
+# ras 9mar08 : pod explaining GT::DB::MetaStockReader and GT::DB::MetaStock usage
 
 use strict;
 use vars qw(@ISA);
@@ -33,6 +34,11 @@
 The MetaStockReader access module is able to retrieve quotes from almost any
 type of MetaStock database.
 
+This module does not require any other code
+to support its operation and it is not intended to be used with
+GT::DB::MetaStock and, although named MetaStockReader, it is not
+the companion program required by GT::DB::MetaStock.
+
 =head2 Synopsis
 
    my $db = create_standard_object("DB::" . GT::Conf::get("DB::module"));
@@ -58,6 +64,9 @@
 
 =head2 Configuration
 
+NOTE: this module supercedes the module GT::DB::MetaStock. do not attempt
+to use both.
+
 You can indicate the directory which contains the MetaStock database
 by setting the DB::metastock::directory configuration item.