App-Chart

 view release on metacpan or  search on metacpan

Makefile.PL  view on Meta::CPAN

   VERSION_FROM => 'lib/App/Chart.pm',
   ABSTRACT     => 'Stock and commodity charting program.',
   AUTHOR       => 'Kevin Ryde <user42_kevin@yahoo.com.au>',
   SIGN         => 1,
   MIN_PERL_VERSION => '5.010',

   # valid LICENSE values per Module::Build::Base::valid_licenses(), or at
   # least the ones there have urls to link
   LICENSE      => 'gpl_3',

   # ExtUtils::MM_Unix "fixin()" changes #!perl to #!/usr/bin/perl
   EXE_FILES    => [ 'chart' ],

   PREREQ_PM     => \%PREREQ_PM,
   TEST_REQUIRES => \%TEST_REQUIRES,

   clean     => { FILES => join(' ',
                                'doc/chart.dvi',
                                'doc/chart.ps',
                                'doc/chart.pdf',
                                'doc/*.dvi',

doc/chart.texi  view on Meta::CPAN

@end table

@c @ifinfo
@c @sp 1
@c @end ifinfo
@section Locale Selection
@cindex Locale selection

@cindex Language
@cindex @env{LANG}
On a typical Unix/POSIX-style system the locale is selected with the
@env{LANG} environment variable, set to a language code and optional territory
and charset.  For example US English,

@example
LANG=en_US
export LANG
@end example

This is often set by the system administrator, but you can do it yourself in
your @file{~/.profile} (@pxref{Bash Startup Files,,, bashref, Bash Features}).

lib/App/Chart/Database/Create.pm  view on Meta::CPAN

my $create_preference = <<'HERE';
CREATE TABLE preference (
    key        TEXT     NOT NULL,
    value      TEXT     NOT NULL,
    PRIMARY KEY (key))
HERE

# DATABASE_SCHEMA_VERSION revisions
#
# Schema 1, version 99.015
#   - "latest" - fetch_timestamp rather than fetch_unixtime
#   - "intraday_image" - fetch_timestamp likewise
#   - "extra" - timestamp strings rather than unixtime numbers
#   - "info" - new exchange column
#   
# Schema 2, version 99.023
#   - "preference" - missed PRIMARY KEY
#
sub upgrade_database {
  my ($dbh, $dbversion) = @_;
  print "Chart: Upgrading database from $dbversion to ",
    App::Chart::DBI::DATABASE_SCHEMA_VERSION(),"\n";
  require App::Chart::Download;

  App::Chart::Database::call_with_transaction
      ($dbh, sub {
         # version 1 adopting timestamps instead of unixtime
         if ($dbversion <= 0) { _upgrade_0_to_1 ($dbh); }
         if ($dbversion <= 1) { _upgrade_1_to_2 ($dbh); }

         $dbh->do ("INSERT OR REPLACE INTO extra (symbol,key,value)
                    VALUES ('','database-schema-version',?)",
                   undef,
                   App::Chart::DBI::DATABASE_SCHEMA_VERSION());
       });
}
sub _upgrade_0_to_1 {
  my ($dbh) = @_;
  $dbh->do ('ALTER TABLE info ADD COLUMN
                      exchange TEXT DEFAULT NULL');

  $dbh->do ('DROP TABLE latest');
  $dbh->do ($create_latest);

  $dbh->do ('DROP TABLE intraday_image');
  $dbh->do ($create_intraday_image);

  $dbh->do ("DELETE FROM extra WHERE key like '%unixtime'");
  App::Chart::Download::consider_latest_from_daily
      ([ App::Chart::Database->symbols_list() ]);
}
sub _upgrade_1_to_2 {
  my $nbh = nbh();
  $nbh->do ('DROP TABLE preference');
  $nbh->do ($create_preference);
}


lib/App/Chart/Series/Derived/PFE.pm  view on Meta::CPAN

      $pfe = $ema_proc->($raw);
    }
    unshift @values, $value;
    if (@values > $N) { pop @values }

    return $pfe;
  };
}

# could use Math::Libm hypot() here for a touch more accuracy, but not sure
# how good its portability is on non-Unix systems
sub _hypot {
  my ($x,$y) = @_;
  return sqrt($x*$x + $y*$y);
}

1;
__END__

# =head1 NAME
# 

lib/App/Chart/Suffix/NZ.pm  view on Meta::CPAN

    // die "NZX dividends parse failed";
  # print JSON->new->pretty->encode($marketDividends), "\n"; exit 0;

  # eg. "amount" : "23.000000000",
  #     "currencyCode" : "NZD",
  #     "imputationCreditAmount" : "0.08166667",
  #     "baseQuantity" : "100",
  #     "expectedDate" : 1724673600
  #     "payableDate"  : 1727352000,
  #     "supplementaryAmount" : "0.03705882",
  # Dates are Unix seconds since 1970 GMT and in NZ timezone is
  # midnight on the relevant date.
  #
  foreach my $href (@$marketDividends) {
    my $isin = $href->{'isin'};
    my $symbol = $isin_to_symbol{$href->{'isin'}}
      // die "NZX dividends, no symbol for ISIN $isin";

    my $amount = App::Chart::Download::trim_decimals
      (App::Chart::Download::cents_to_dollars($href->{'amount'}),
       2);

lib/App/Chart/Yahoo.pm  view on Meta::CPAN

}


#------------------------------------------------------------------------------
# Latest
#
# This uses for example
#
#     https://query1.finance.yahoo.com/v7/finance/chart/BHP.AX?period1=1718841600&period2=1719532800&interval=1d&events=history&close=unadjusted
#
# periodi1 and period2 are Unix style seconds since 1 Jan 1970 GMT.
#
# https://stackoverflow.com/questions/47076404/currency-helper-of-yahoo-sorry-unable-to-process-request-at-this-time-erro
# ->
# https://stackoverflow.com/questions/47064776/has-yahoo-suddenly-today-terminated-its-finance-download-api
#
# FUTURE: Intending to switch this over to v8 the same as the daily
# data, and possibly a single common parse.  But this latest quote
# way has survived without problem during cooking and crumb troubles,
# not don't need to rush to change what's working.

lib/App/Chart/Yahoo.pm  view on Meta::CPAN

   proc => \&latest_download,
   max_symbols => 1);  # downloads go 1 at a time

sub latest_download {
  my ($symbol_list) = @_;

  foreach my $symbol (@$symbol_list) {
    my $tdate = daily_available_tdate ($symbol);
    App::Chart::Download::status(__('Yahoo quote'), $symbol);

    my $lo_timet = tdate_to_unix($tdate - 4);
    my $hi_timet = tdate_to_unix($tdate + 2);

    my $events = 'history';
    my $url = "https://query1.finance.yahoo.com/v7/finance/chart/"
      . URI::Escape::uri_escape($symbol)
      ."?period1=$lo_timet"
      ."&period2=$hi_timet"
      ."&interval=1d"
      ."&events=$events"
      ."&close=unadjusted";

lib/App/Chart/Yahoo.pm  view on Meta::CPAN

}


#-----------------------------------------------------------------------------
# Download Data, including dividends and splits
#
# This uses the "v8" historical prices downloads in JSON format like
#
#     https://query2.finance.yahoo.com/v8/finance/chart/IBM?period1=1504028419&period2=1504428419&interval=1d&events=div%7Csplit&close=unadjusted
#
# period1 is the start time, period2 the end time, both as Unix
# seconds since 1 Jan 1970 in GMT.
#
# close=unadjusted means prices are without any adjustment for
# splits, so prices as traded at the time.
#
# If no trading in the date range (eg. before first listing) then
#
#     400 Bad Request
#     {"chart":{"result":null,"error":{"code":"Bad Request","description":"Data doesn't exist for startDate = 1565827200, endDate = 1596153600"}}}
#

lib/App/Chart/Yahoo.pm  view on Meta::CPAN


  # As of September 2017, daily data is present for the current
  # day's trade, during the trading session.
  # Try reckoning it complete at 6pm.
  return App::Chart::Download::tdate_today_after
    (18,0, App::Chart::TZ->for_symbol ($symbol));
}

sub daily_url_func {
  my ($symbol, $lo_tdate, $hi_tdate) = @_;
  my $lo_timet = tdate_to_unix($lo_tdate - 2);
  my $hi_timet = tdate_to_unix($hi_tdate);

  # As of September 2024, dividends only appear on (or after?)
  # the ex date.  But try hi_timet well ahead hoping for
  # upcoming dividends (ex date announced).
  if ($hi_tdate >= daily_available_tdate($symbol)) {
    $hi_timet += 60 * 86400;
  }

  return "https://query1.finance.yahoo.com/v8/finance/chart/"
    . URI::Escape::uri_escape($symbol)

lib/App/Chart/Yahoo.pm  view on Meta::CPAN

    $got = 0;
    $str .= '.';
  }
  if ((my $add = $decimals - $got) > 0) {
    $str .= '0' x $add;
  }
  return $str;
}

# Return seconds since 00:00:00, 1 Jan 1970 GMT.
sub tdate_to_unix {
  my ($tdate) = @_;
  my $adate = App::Chart::tdate_to_adate ($tdate);
  return ($adate + 4)*86400;
}

#------------------------------------------------------------------------------
1;
__END__

lib/App/Chart/doc/chart.html  view on Meta::CPAN


<ul class="mini-toc">
<li><a href="#Locale-Selection" accesskey="1">Locale Selection</a></li>
</ul>
<div class="section-level-extent" id="Locale-Selection">
<h3 class="section"><span>17.1 Locale Selection<a class="copiable-link" href="#Locale-Selection"> &para;</a></span></h3>
<a class="index-entry-id" id="index-Locale-selection"></a>

<a class="index-entry-id" id="index-Language"></a>
<a class="index-entry-id" id="index-LANG"></a>
<p>On a typical Unix/POSIX-style system the locale is selected with the
<code class="env">LANG</code> environment variable, set to a language code and optional territory
and charset.  For example US English,
</p>
<div class="example">
<pre class="example-preformatted">LANG=en_US
export LANG
</pre></div>

<p>This is often set by the system administrator, but you can do it yourself in
your <samp class="file">~/.profile</samp> (see <a data-manual="bashref" href="http://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html">Bash Startup Files</a> in <cite class="cite">Bash Features</cite>).

maybe/unused.pm  view on Meta::CPAN



#------------------------------------------------------------------------------

sub delete_extra {
  my ($symbol, $key) = @_;
  my $dbh = App::Chart::DBI->instance;
  $dbh->do ('DELETE FROM extra WHERE symbol=? AND key=?', {}, $symbol, $key);
}

sub strftime_unixtime_local {
  my ($format, $unixtime) = @_;
  my $time = $unixtime + $App::Chart::unixtime_base;
  return App::Chart::strftime_wide ($format, localtime ($time));
}

#------------------------------------------------------------------------------

=item App::Chart::unixtime ()

Return the current time in Unix style seconds since midnight 1 Jan
1970.  This function always uses 1970 as the epoch, unlike C<time()> which
is either 1970 or 1904 depending on the platform (1904 on MacOS).

=cut

our $unixtime_base = Date::Calc::Date_to_Time (1970,1,1, 0,0,0);
sub unixtime {
  return (time () - $unixtime_base);
}


misc/t-yahoo.pl  view on Meta::CPAN

  # SCT.AX Scout Security, 1 old -> 100 new consolidation Aug 2024
  # As of September 2024, split appears 4 times
  # Think one only 5 Aug 24 price 0.52 up from 0.0052
  # 1718409600
  # 1719446400

  my $symbol = 'SCT.AX';
  $symbol = 'ORD.AX';
  my $lo_tdate = App::Chart::ymd_to_tdate_floor(2024,6,1);
  my $hi_tdate = App::Chart::ymd_to_tdate_floor(2024,9,1);
  my $lo_timet = App::Chart::Yahoo::tdate_to_unix($lo_tdate);
  my $hi_timet = App::Chart::Yahoo::tdate_to_unix($hi_tdate);

  my $url = "https://query1.finance.yahoo.com/v8/finance/chart/"
    . URI::Escape::uri_escape($symbol)
    ."?period1=$lo_timet"
    ."&period2=$hi_timet"
    ."&interval=1d"
    ."&events=". URI::Escape::uri_escape('div|split')
    ."&close=unadjusted";
  print "$url\n";

misc/t-yahoo.pl  view on Meta::CPAN

}

{
  # v8 dividends ahead
  # https://www.intelligentinvestor.com.au/investment-tools/shares/dividends
  # https://query2.finance.yahoo.com/v8/finance/chart/IBM?formatted=true&lang=en-US&region=US&period1=1487372400&period2=1495058400&interval=1d&events=div%7Csplit&corsDomain=finance.yahoo.com

  my $symbol = 'NKLA.MX';
  $symbol = 'WBCPK.AX';
  my $tdate = App::Chart::Yahoo::daily_available_tdate ($symbol);
  my $lo_timet = App::Chart::Yahoo::tdate_to_unix($tdate - 30);
  my $hi_timet = App::Chart::Yahoo::tdate_to_unix($tdate + 100);

  my $url = "https://query1.finance.yahoo.com/v8/finance/chart/"
    . URI::Escape::uri_escape($symbol)
    ."?period1=$lo_timet"
    ."&period2=$hi_timet"
    ."&interval=1d"
    ."&events=". URI::Escape::uri_escape('div|split')
    ."&close=unadjusted";
  print "$url\n";

t/Yahoo.t  view on Meta::CPAN


# uncomment this to run the ### lines
# use Smart::Comments;

if ($have_test_mocktime) {
  diag "Test::MockTime version ", Test::MockTime->VERSION;
  diag "Test::MockTime::DateCalc version ", Test::MockTime::DateCalc->VERSION;
}

#------------------------------------------------------------------------------
# tdate_to_unix()

# 1 Jan 1970  Thu   0*secsperday
# 2 Jan 1970  Fri   1
# 3 Jan 1970  Sat   2
# 4 Jan 1970  Sun   3
# 5 Jan 1970  Mon   4
#
is (App::Chart::Yahoo::tdate_to_unix(0), 4*86400,
    "tdate 0 is 5 Jan 1970");

{
  my $base_adate = App::Chart::ymd_to_adate(1970,1,1);
  is ($base_adate, -4, "adate of 1 Jan 1970");
  my $tdate = 12345;
  my $got = App::Chart::Yahoo::tdate_to_unix($tdate);
  my $want = (App::Chart::tdate_to_adate($tdate) - $base_adate) * 86400;
  is ($got, $want, 'tdate_to_unix()');
}


#------------------------------------------------------------------------------
# round_decimals()

is (App::Chart::Yahoo::round_decimals('1.23456',1), '1.2');
is (App::Chart::Yahoo::round_decimals('1.23456',2), '1.23');
is (App::Chart::Yahoo::round_decimals('1.23456',3), '1.235');
is (App::Chart::Yahoo::round_decimals('1.23456',4), '1.2346');

unused/Yahoo-v7.pm  view on Meta::CPAN

}


#------------------------------------------------------------------------------
# Latest
#
# This uses for example
#
#     https://query1.finance.yahoo.com/v7/finance/chart/BHP.AX?period1=1718841600&period2=1719532800&interval=1d&events=history&close=unadjusted
#
# periodi1 and period2 are Unix style seconds since 1 Jan 1970 GMT.
#
# https://stackoverflow.com/questions/47076404/currency-helper-of-yahoo-sorry-unable-to-process-request-at-this-time-erro
# ->
# https://stackoverflow.com/questions/47064776/has-yahoo-suddenly-today-terminated-its-finance-download-api

App::Chart::LatestHandler->new
  (pred => $latest_pred,
   proc => \&latest_download,
   max_symbols => 1);  # downloads go 1 at a time

sub latest_download {
  my ($symbol_list) = @_;

  foreach my $symbol (@$symbol_list) {
    my $tdate = daily_available_tdate ($symbol);
    App::Chart::Download::status(__('Yahoo quote'), $symbol);

    my $lo_timet = tdate_to_unix($tdate - 4);
    my $hi_timet = tdate_to_unix($tdate + 2);

    my $events = 'history';
    my $url = "https://query1.finance.yahoo.com/v7/finance/chart/"
      . URI::Escape::uri_escape($symbol)
      ."?period1=$lo_timet"
      ."&period2=$hi_timet"
      ."&interval=1d"
      ."&events=$events"
      ."&close=unadjusted";

unused/Yahoo-v7.pm  view on Meta::CPAN

# and contains buried within 1.5 mbytes of hideous script
#
#    <script type="application/json" data-sveltekit-fetched data-url="https://query1.finance.yahoo.com/v1/test/getcrumb?lang=en-US&amp;region=US" data-ttl="59">{"status":200,"statusText":"OK","headers":{},"body":"DKVWQE/ggh4"}</script>
#
# Any \u002F or similar is escaped "/" character or similar.
# The crumb is included in a CSV download query like the following
# (alas can't use http, it redirects to https)
#
#     https://query1.finance.yahoo.com/v7/finance/download/AMP.AX?period1=1503810440&period2=1504415240&interval=1d&events=history&crumb=hdDX/HGsZ0Q
#
# period1 is the start time, period2 the end time, both as Unix seconds
# since 1 Jan 1970.  Not sure of the timezone needed.  Some experiments
# suggest it depends on the timezone of the symbol.  http works as well as
# https.  The result is like
#
#     Date,Open,High,Low,Close,Adj Close,Volume
#     2017-09-07,30.299999,30.379999,30.000000,30.170000,30.170000,3451099
#
# The "9999s" are some bad rounding off to what would be usually at most
# 3 (maybe 4?) decimal places.
#

unused/Yahoo-v7.pm  view on Meta::CPAN


  my $crumb_errors = 0;
 SYMBOL: foreach my $symbol (@$symbol_list) {
    my $lo_tdate = App::Chart::Download::start_tdate_for_update (@$symbol_list);
    my $hi_tdate = daily_available_tdate ($symbol);

    App::Chart::Download::status
        (__('Yahoo data'), $symbol,
         App::Chart::Download::tdate_range_string ($lo_tdate, $hi_tdate));

    my $lo_timet = tdate_to_unix($lo_tdate - 2);
    my $hi_timet = tdate_to_unix($hi_tdate + 2);

    # my $data  = cookie_and_crumb_data();
    # if (! defined $data) {
    #   print "Yahoo $symbol no daily cookie data\n";
    #   next SYMBOL;
    # }
    # my $crumb = URI::Escape::uri_escape($data->{'crumb'});
    # my $jar = http_cookies_from_string($data->{'cookies'} // '');

    my $h = { source          => __PACKAGE__,

unused/Yahoo-v7.pm  view on Meta::CPAN


sub timezone_gmtoffset_at_ymd {
  my ($timezone, $year, $month, $day) = @_;
  my $timet = $timezone->call(\&POSIX::mktime,
                              0, 0, 0, $day, $month-1, $year-1900);
  my ($sec,$min,$hour,$gmt_day) = gmtime($timet);
  return $sec + 60*$min + 3600*$hour + 86400*($gmt_day - $day);
}

# Return seconds since 00:00:00, 1 Jan 1970 GMT.
sub tdate_to_unix {
  my ($tdate) = @_;
  my $adate = App::Chart::tdate_to_adate ($tdate);
  return ($adate + 4)*86400;
}

# $str is a string from previous HTTP::Cookies ->as_string()
# Return a new HTTP::Cookies object with that content.
sub http_cookies_from_string {
  my ($str) = @_;
  require File::Temp;

unused/YahooOld.pm  view on Meta::CPAN

# 
#   # App::Chart::Download::verbose_message ("Yahoo crumb $crumb cookies\n"
#   #                                        . $jar->as_string);
# 
#   my $crumb_errors = 0;
#  SYMBOL: foreach my $symbol (@$symbol_list) {
#     my $tdate = daily_available_tdate ($symbol);
# 
#     App::Chart::Download::status(__('Yahoo quote'), $symbol);
# 
#     my $lo_timet = tdate_to_unix($tdate - 4);
#     my $hi_timet = tdate_to_unix($tdate + 2);
# 
#     my $data  = daily_cookie_data($symbol);
#     if (! defined $data) {
#       print "Yahoo $symbol does not exist\n";
#       next SYMBOL;
#     }
#     my $crumb = URI::Escape::uri_escape($data->{'crumb'});
#     my $jar = http_cookies_from_string($data->{'cookies'} // '');
# 
#     my $events = 'history';

unused/YahooOld.pm  view on Meta::CPAN

# and contains buried within 1.5 mbytes of hideous script
#
#    <script type="application/json" data-sveltekit-fetched data-url="https://query1.finance.yahoo.com/v1/test/getcrumb?lang=en-US&amp;region=US" data-ttl="59">{"status":200,"statusText":"OK","headers":{},"body":"DKVWQE/ggh4"}</script>
#
# Any \u002F or similar is escaped "/" character or similar.
# The crumb is included in a CSV download query like the following
# (alas can't use http, it redirects to https)
#
#     https://query1.finance.yahoo.com/v7/finance/download/AMP.AX?period1=1503810440&period2=1504415240&interval=1d&events=history&crumb=hdDX/HGsZ0Q
#
# period1 is the start time, period2 the end time, both as Unix seconds
# since 1 Jan 1970.  Not sure of the timezone needed.  Some experiments
# suggest it depends on the timezone of the symbol.  http works as well as
# https.  The result is like
#
#     Date,Open,High,Low,Close,Adj Close,Volume
#     2017-09-07,30.299999,30.379999,30.000000,30.170000,30.170000,3451099
#
# The "9999s" are some bad rounding off to what would be usually at most
# 3 (maybe 4?) decimal places.
#

unused/YahooOld.pm  view on Meta::CPAN


  my $crumb_errors = 0;
 SYMBOL: foreach my $symbol (@$symbol_list) {
    my $lo_tdate = App::Chart::Download::start_tdate_for_update (@$symbol_list);
    my $hi_tdate = daily_available_tdate ($symbol);

    App::Chart::Download::status
        (__('Yahoo data'), $symbol,
         App::Chart::Download::tdate_range_string ($lo_tdate, $hi_tdate));

    my $lo_timet = tdate_to_unix($lo_tdate - 2);
    my $hi_timet = tdate_to_unix($hi_tdate + 2);

    my $data  = daily_cookie_data($symbol);
    if (! defined $data) {
      print "Yahoo $symbol no daily cookie data\n";
      next SYMBOL;
    }
    my $crumb = URI::Escape::uri_escape($data->{'crumb'});
    my $jar = http_cookies_from_string($data->{'cookies'} // '');

    my $h = { source          => __PACKAGE__,



( run in 1.276 second using v1.01-cache-2.11-cpan-64ef6c95b5d )