view release on metacpan or search on metacpan
Makefile.PL
README
cpanfile
dist.ini
lib/Finance/Calendar.pm
lib/Finance/Calendar.pod
t/00-check-deps.t
t/00-compile.t
t/00-report-prereqs.dd
t/00-report-prereqs.t
t/calendar.t
t/rc/perlcriticrc
t/rc/perltidyrc
t/tactical_exchanges.t
xt/author/critic.t
xt/author/distmeta.t
xt/author/eol.t
xt/author/minimum-version.t
xt/author/mojibake.t
xt/author/no-tabs.t
xt/author/pod-syntax.t
{
   "abstract" : "represents the trading calendar.",
   "author" : [
      "binary.com <BINARY@cpan.org>"
   ],
   "dynamic_config" : 0,
   "generated_by" : "Dist::Zilla version 6.029, CPAN::Meta::Converter version 2.150010",
   "license" : [
      "perl_5"
   ],
   "meta-spec" : {
      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
---
abstract: 'represents the trading calendar.'
author:
  - 'binary.com <BINARY@cpan.org>'
build_requires:
  ExtUtils::MakeMaker: '0'
  File::Spec: '0'
  IO::Handle: '0'
  IPC::Open3: '0'
  Test::CheckDeps: '0.010'
  Test::FailWarnings: '0.008'
  Test::MockTime: '0'
Makefile.PL view on Meta::CPAN
# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.029.
use strict;
use warnings;
use 5.006;
use ExtUtils::MakeMaker 7.64;
my %WriteMakefileArgs = (
  "ABSTRACT" => "represents the trading calendar.",
  "AUTHOR" => "binary.com <BINARY\@cpan.org>",
  "CONFIGURE_REQUIRES" => {
    "ExtUtils::MakeMaker" => "7.64"
  },
  "DISTNAME" => "Finance-Calendar",
  "LICENSE" => "perl",
  "MIN_PERL_VERSION" => "5.006",
  "NAME" => "Finance::Calendar",
  "PREREQ_PM" => {
    "Carp" => 0,
NAME
    Finance::Calendar - represents the trading calendar.
SYNOPSIS
        use Finance::Calendar;
        use Date::Utility;
    
        my $calendar = {
            holidays => {
                "25-Dec-2013" => {
                    "Christmas Day" => [qw(FOREX METAL)],
                },
                "1-Jan-2014" => {
                    "New Year's Day" => [qw( FOREX METAL)],
                },
                "1-Apr-2013" => {
                    "Easter Monday" => [qw( USD)],
                },
                '22-Dec-2016' => {
                    '18:00' => ['FOREX', 'METAL'],
                },
            },
            late_opens => {
                '24-Dec-2010' => {
                    '14:30' => ['HKSE'],
                },
            },
        };
        my $calendar = Finance::Calendar->new(calendar => $calendar);
        my $now = Date::Utility->new;
    
        # Does London Stocks Exchange trade on $now
        $calendar->trades_on(Finance::Exchange->create_exchange('LSE'), $now);
    
        # Is it a country holiday for the United States on $now
        $calendar->is_holiday_for('USD', $now);
    
        # Returns the opening time of Australian Stocks Exchange on $now
        $calendar->opening_on(Finance::Exchange->create_exchange('ASX'), $now);
    
        # Returns the closing time of Forex on $now
        $calendar->closing_on(Finance::Exchange->create_exchange('FOREX'), $now);
        ...
DESCRIPTION
    This class is responsible for providing trading times or holidays
    related information of a given financial stock exchange on a specific
    date.
ATTRIBUTES - Object Construction
 calendar
    A hash reference that has information on: - exchange and country
    holidays - late opens - early closes
METHODS - TRADING DAYS RELATED
 trades_on
    ->trades_on($exchange_object, $date_object);
    ->trading_date_for($exchange_object, $date_object);
    The date on which trading is considered to be taking place even if it
    is not the same as the GMT date. Note that this does not handle trading
    dates are offset forward beyond the next day (24h). It will need
    additional work if these are found to exist.
    Returns a Date object representing midnight GMT of the trading date.
 calendar_days_to_trade_date_after
    ->calendar_days_to_trade_date_after($exchange_object, $date_object);
    Returns the number of calendar days between a given Date::Utility and
    the next day on which trading is open.
 trading_days_between
    ->trading_days_between($exchange_object,
    Date::Utility->new('4-May-10'),Date::Utility->new('5-May-10'));
    Returns the number of trading days _between_ two given dates.
 holiday_days_between
    trading days. Currently, this applies on: - Sundays (for RSI exchanges
    opening times) - Fridays (for closing times).
    exchange - a Finance::Exchange instance
    when - a Date::Utility instance
    Examples:
        # Friday closing adjustment for FOREX
        my $changes = $calendar->regularly_adjusts_trading_hours_on(
            Finance::Exchange->create_exchange('FOREX'),
            Date::Utility->new('2023-02-17')  # Friday
        );
        # Returns a hashref with adjusted closing time on Fridays:
        # {
        #     'daily_close' => {
        #         'to' => '20h55m',
        #         'rule' => 'Fridays'
        #     }
        # }
    
        # Sunday opening adjustment for RSI exchanges
        my $changes = $calendar->regularly_adjusts_trading_hours_on(
            Finance::Exchange->create_exchange('TACTICAL_FOREX_EURUSD'),
            Date::Utility->new('2023-02-12')  # Sunday
        );
        # Returns a hashref with adjusted opening time on Sundays:
        # {
        #     'daily_open' => {
        #         'to' => '22h35m',
        #         'rule' => 'Sundays'  # or 'Sundays (DST)' if in DST
        #     }
        # }
lib/Finance/Calendar.pm view on Meta::CPAN
package Finance::Calendar;
=head1 NAME
Finance::Calendar - represents the trading calendar.
=head1 SYNOPSIS
    use Finance::Calendar;
    use Date::Utility;
    my $calendar = {
        holidays => {
            "25-Dec-2013" => {
                "Christmas Day" => [qw(FOREX METAL)],
            },
            "1-Jan-2014" => {
                "New Year's Day" => [qw( FOREX METAL)],
            },
            "1-Apr-2013" => {
                "Easter Monday" => [qw( USD)],
            },
lib/Finance/Calendar.pm view on Meta::CPAN
            '22-Dec-2016' => {
                '18:00' => ['FOREX', 'METAL'],
            },
        },
        late_opens => {
            '24-Dec-2010' => {
                '14:30' => ['HKSE'],
            },
        },
    };
    my $calendar = Finance::Calendar->new(calendar => $calendar);
    my $now = Date::Utility->new;
    # Does London Stocks Exchange trade on $now
    $calendar->trades_on(Finance::Exchange->create_exchange('LSE'), $now);
    # Is it a country holiday for the United States on $now
    $calendar->is_holiday_for('USD', $now);
    # Returns the opening time of Australian Stocks Exchange on $now
    $calendar->opening_on(Finance::Exchange->create_exchange('ASX'), $now);
    # Returns the closing time of Forex on $now
    $calendar->closing_on(Finance::Exchange->create_exchange('FOREX'), $now);
    ...
=head1 DESCRIPTION
This class is responsible for providing trading times or holidays related information of a given financial stock exchange on a specific date.
=cut
use Moose;
our $VERSION = '0.07';
use List::Util qw(min max first);
use Date::Utility;
use Memoize;
use Finance::Exchange;
use Carp qw(croak);
=head1 ATTRIBUTES - Object Construction
=head2 calendar
A hash reference that has information on:
- exchange and country holidays
- late opens
- early closes
=cut
has calendar => (
    is       => 'ro',
    required => 1,
);
has _cache => (
    is      => 'ro',
    default => sub { {} },
);
sub _get_cache {
lib/Finance/Calendar.pm view on Meta::CPAN
    return $date->truncate_to_day unless ($exchange->trading_date_can_differ);
    my $next_day = $date->plus_time_interval('1d')->truncate_to_day;
    my $open_ti =
        $exchange->market_times->{$self->_times_dst_key($exchange, $next_day)}->{daily_open};
    return $next_day if ($open_ti and $next_day->epoch + $open_ti->seconds <= $date->epoch);
    return $date->truncate_to_day;
}
=head2 calendar_days_to_trade_date_after
->calendar_days_to_trade_date_after($exchange_object, $date_object);
Returns the number of calendar days between a given Date::Utility
and the next day on which trading is open.
=cut
sub calendar_days_to_trade_date_after {
    my ($self, $exchange, $when) = @_;
    if (my $cache = $self->_get_cache('calendar_days_to_trade_date_after', $exchange, $when)) {
        return $cache;
    }
    my $number_of_days = $self->trade_date_after($exchange, $when)->days_between($when);
    $self->_set_cache($number_of_days, 'calendar_days_to_trade_date_after', $exchange, $when);
    return $number_of_days;
}
=head2 trading_days_between
->trading_days_between($exchange_object, Date::Utility->new('4-May-10'),Date::Utility->new('5-May-10'));
Returns the number of trading days _between_ two given dates.
lib/Finance/Calendar.pm view on Meta::CPAN
=item C<exchange> - a L<Finance::Exchange> instance
=item C<when> - a L<Date::Utility> instance
=back
Examples:
    # Friday closing adjustment for FOREX
    my $changes = $calendar->regularly_adjusts_trading_hours_on(
        Finance::Exchange->create_exchange('FOREX'),
        Date::Utility->new('2023-02-17')  # Friday
    );
    # Returns a hashref with adjusted closing time on Fridays:
    # {
    #     'daily_close' => {
    #         'to' => '20h55m',
    #         'rule' => 'Fridays'
    #     }
    # }
    # Sunday opening adjustment for RSI exchanges
    my $changes = $calendar->regularly_adjusts_trading_hours_on(
        Finance::Exchange->create_exchange('TACTICAL_FOREX_EURUSD'),
        Date::Utility->new('2023-02-12')  # Sunday
    );
    # Returns a hashref with adjusted opening time on Sundays:
    # {
    #     'daily_open' => {
    #         'to' => '22h35m',
    #         'rule' => 'Sundays'  # or 'Sundays (DST)' if in DST
    #     }
    # }
lib/Finance/Calendar.pm view on Meta::CPAN
    return Date::Utility->new($epoch)->is_dst_in_zone($exchange->trading_timezone);
}
### PRIVATE ###
sub _get_holidays_for {
    my ($self, $symbol, $when) = @_;
    my $date     = $when->truncate_to_day->epoch;
    my $calendar = $self->calendar->{holidays};
    my $holiday  = $calendar->{$date};
    return undef unless $holiday;
    foreach my $holiday_desc (keys %$holiday) {
        return $holiday_desc if (first { $symbol eq $_ } @{$holiday->{$holiday_desc}});
    }
    return undef;
}
lib/Finance/Calendar.pm view on Meta::CPAN
    my $epoch = (ref $when) ? $when->epoch : $when;
    return 'dst' if $self->is_in_dst_at($exchange, $epoch);
    return 'standard';
}
# get partial trading data for a given exchange
sub _get_partial_trading_for {
    my ($self, $exchange, $type, $when) = @_;
    my $cached          = $self->calendar->{$type};
    my $date            = $when->truncate_to_day->epoch;
    my $partial_defined = $cached->{$date};
    return undef unless $partial_defined;
    foreach my $close_time (keys %{$cached->{$date}}) {
        my $symbols = $cached->{$date}{$close_time};
        return $close_time if (first { $exchange->symbol eq $_ } @$symbols);
    }
lib/Finance/Calendar.pm view on Meta::CPAN
# - opened : undefined if market is closed, contains the seconds the market has
#            been open.
#
#
########
sub _market_opens {
    my ($self, $exchange, $when) = @_;
    my $date = $when;
    # Figure out which "trading day" we are on
    # even if it differs from the GMT calendar day.
    my $next_day  = $date->plus_time_interval('1d')->truncate_to_day;
    my $next_open = $self->opening_on($exchange, $next_day);
    $date = $next_day if ($next_open and not $date->is_before($next_open));
    my $open  = $self->opening_on($exchange, $date);
    my $close = $self->closing_on($exchange, $date);
    if (not $open) {
        # date is not a trading day: will not and has not been open today
lib/Finance/Calendar.pod view on Meta::CPAN
this file, but rather the original, inline with Finance::Calendar
at lib/Finance/Calendar.pm
(on the system that originally ran this).
If you do edit this file, and don't want your changes to be removed, make
sure you change the first line.
=cut
=head1 NAME
Finance::Calendar - represents the trading calendar.
=head1 SYNOPSIS
    use Finance::Calendar;
    use Date::Utility;
    my $calendar = {
        holidays => {
            "25-Dec-2013" => {
                "Christmas Day" => [qw(FOREX METAL)],
            },
            "1-Jan-2014" => {
                "New Year's Day" => [qw( FOREX METAL)],
            },
            "1-Apr-2013" => {
                "Easter Monday" => [qw( USD)],
            },
lib/Finance/Calendar.pod view on Meta::CPAN
            '22-Dec-2016' => {
                '18:00' => ['FOREX', 'METAL'],
            },
        },
        late_opens => {
            '24-Dec-2010' => {
                '14:30' => ['HKSE'],
            },
        },
    };
    my $calendar = Finance::Calendar->new(calendar => $calendar);
    my $now = Date::Utility->new;
    # Does London Stocks Exchange trade on $now
    $calendar->trades_on(Finance::Exchange->create_exchange('LSE'), $now);
    # Is it a country holiday for the United States on $now
    $calendar->is_holiday_for('USD', $now);
    # Returns the opening time of Australian Stocks Exchange on $now
    $calendar->opening_on(Finance::Exchange->create_exchange('ASX'), $now);
    # Returns the closing time of Forex on $now
    $calendar->closing_on(Finance::Exchange->create_exchange('FOREX'), $now);
    ...
=head1 DESCRIPTION
This class is responsible for providing trading times or holidays related information of a given financial stock exchange on a specific date.
=head1 ATTRIBUTES - Object Construction
=head2 calendar
A hash reference that has information on:
- exchange and country holidays
- late opens
- early closes
=head1 METHODS - TRADING DAYS RELATED
=head2 trades_on
lib/Finance/Calendar.pod view on Meta::CPAN
=head2 trading_date_for
->trading_date_for($exchange_object, $date_object);
The date on which trading is considered to be taking place even if it is not the same as the GMT date.
Note that this does not handle trading dates are offset forward beyond the next day (24h). It will need additional work if these are found to exist.
Returns a Date object representing midnight GMT of the trading date.
=head2 calendar_days_to_trade_date_after
->calendar_days_to_trade_date_after($exchange_object, $date_object);
Returns the number of calendar days between a given Date::Utility
and the next day on which trading is open.
=head2 trading_days_between
->trading_days_between($exchange_object, Date::Utility->new('4-May-10'),Date::Utility->new('5-May-10'));
Returns the number of trading days _between_ two given dates.
=head2 holiday_days_between
lib/Finance/Calendar.pod view on Meta::CPAN
=item C<exchange> - a L<Finance::Exchange> instance
=item C<when> - a L<Date::Utility> instance
=back
Examples:
    # Friday closing adjustment for FOREX
    my $changes = $calendar->regularly_adjusts_trading_hours_on(
        Finance::Exchange->create_exchange('FOREX'),
        Date::Utility->new('2023-02-17')  # Friday
    );
    # Returns a hashref with adjusted closing time on Fridays:
    # {
    #     'daily_close' => {
    #         'to' => '20h55m',
    #         'rule' => 'Fridays'
    #     }
    # }
    # Sunday opening adjustment for RSI exchanges
    my $changes = $calendar->regularly_adjusts_trading_hours_on(
        Finance::Exchange->create_exchange('TACTICAL_FOREX_EURUSD'),
        Date::Utility->new('2023-02-12')  # Sunday
    );
    # Returns a hashref with adjusted opening time on Sundays:
    # {
    #     'daily_open' => {
    #         'to' => '22h35m',
    #         'rule' => 'Sundays'  # or 'Sundays (DST)' if in DST
    #     }
    # }
t/calendar.t view on Meta::CPAN
use Test::MockTime qw( :all );
use Test::Most;
use Test::FailWarnings;
use Time::Local ();
use Finance::Exchange;
use Finance::Calendar;
my $date = Date::Utility->new('2013-12-01');    # first of December 2014
my $calendar = {
    holidays => {
        1367798400 => {
            "Early May Bank Holiday" => [qw(LSE)],
        },
        1387929600 => {
            "Christmas Day" => [qw(LSE FOREX METAL)],
        },
        1388534400 => {
            "New Year's Day" => [qw(LSE FOREX METAL)],
        },
t/calendar.t view on Meta::CPAN
            '18h' => ['FOREX', 'METAL'],
        },
    },
    late_opens => {
        1293148800 => {
            '2h30m' => ['HKSE'],
        },
    },
};
my $tc = Finance::Calendar->new(calendar => $calendar);
my ($LSE, $RANDOM, $FOREX, $ASX, $HKSE, $METAL, $JSC, $SWX) =
    map { Finance::Exchange->create_exchange($_) } qw(LSE RANDOM FOREX ASX HKSE METAL JSC SWX);
subtest 'trades_on' => sub {
    ok !$tc->trades_on($LSE,   Date::Utility->new('1-Jan-14')),  'LSE doesn\'t trade on 1-Jan-14 because it is on holiday.';
    ok !$tc->trades_on($LSE,   Date::Utility->new('12-May-13')), 'LSE doesn\'t trade on weekend (12-May-13).';
    ok $tc->trades_on($LSE,    Date::Utility->new('3-May-13')),  'LSE trades on normal day 4-May-13.';
    ok !$tc->trades_on($LSE,   Date::Utility->new('5-May-13')),  'LSE doesn\'t trade on 5-May-13 as it is a weekend.';
    ok $tc->trades_on($RANDOM, Date::Utility->new('5-May-13')),  'RANDOM trades on 5-May-13 as it is open on weekends.';
};
t/calendar.t view on Meta::CPAN
subtest 'trading_date_for' => sub {
    my $non_dst_asx = Date::Utility->new('2017-09-30 15:59:59');
    my $dst_asx     = Date::Utility->new('2017-09-30 16:00:00');
    ok !$tc->is_in_dst_at($ASX, $non_dst_asx->epoch);
    ok $tc->is_in_dst_at($ASX,  $dst_asx->epoch);
    is $tc->trading_date_for($ASX, $non_dst_asx)->epoch, $non_dst_asx->truncate_to_day->epoch, 'same day on non dst';
    is $tc->trading_date_for($ASX, $dst_asx)->epoch,     $dst_asx->truncate_to_day->epoch,     'same day if time is before open on dst';
    is $tc->trading_date_for($ASX, $dst_asx->plus_time_interval('7h'))->epoch, $dst_asx->plus_time_interval('1d')->truncate_to_day->epoch, 'next day';
};
subtest 'calendar_days_to_trade_date_after' => sub {
    is($tc->calendar_days_to_trade_date_after($FOREX, Date::Utility->new('20-Dec-13')),
        3, '3 calendar days until next trading day on FOREX after 20-Dec-13');
    is($tc->calendar_days_to_trade_date_after($FOREX, Date::Utility->new('27-Dec-13')),
        3, '3 calendar days until next trading day on FOREX after 27-Dec-13');
    is($tc->calendar_days_to_trade_date_after($FOREX, Date::Utility->new('7-Mar-13')),
        1, '1 calendar day until next trading day on FOREX after 7-Mar-13');
    is($tc->calendar_days_to_trade_date_after($FOREX, Date::Utility->new('8-Mar-13')),
        3, '3 calendar days until next trading day on FOREX after 8-Mar-13');
    is($tc->calendar_days_to_trade_date_after($FOREX, Date::Utility->new('9-Mar-13')),
        2, '2 calendar days until next trading day on FOREX after 9-Mar-13');
    is($tc->calendar_days_to_trade_date_after($FOREX, Date::Utility->new('10-Mar-13')),
        1, '1 calendar day until next trading day on FOREX after 10-Mar-13');
};
subtest 'trading_days_between' => sub {
    is($tc->trading_days_between($LSE, Date::Utility->new('29-Mar-13'), Date::Utility->new('1-Apr-13')),
        0, 'No trading days between 29th Mar and 1st Apr on LSE');
    is($tc->trading_days_between($LSE, Date::Utility->new('11-May-13'), Date::Utility->new('12-May-13')),
        0, 'No trading days between 11th and 12th May on LSE (over weekend)');
    is($tc->trading_days_between($LSE, Date::Utility->new('4-May-13'), Date::Utility->new('6-May-13')),
        0, 'No trading days between 4th May and 6th May on LSE (over weekend, then holiday on Monday)');
    is($tc->trading_days_between($LSE, Date::Utility->new('10-May-13'), Date::Utility->new('14-May-13')),
xt/author/eol.t view on Meta::CPAN
use Test::More 0.88;
use Test::EOL;
my @files = (
    'lib/Finance/Calendar.pm',
    'lib/Finance/Calendar.pod',
    't/00-check-deps.t',
    't/00-compile.t',
    't/00-report-prereqs.dd',
    't/00-report-prereqs.t',
    't/calendar.t',
    't/rc/perlcriticrc',
    't/rc/perltidyrc',
    't/tactical_exchanges.t',
    'xt/author/critic.t',
    'xt/author/distmeta.t',
    'xt/author/eol.t',
    'xt/author/minimum-version.t',
    'xt/author/mojibake.t',
    'xt/author/no-tabs.t',
    'xt/author/pod-syntax.t',
xt/author/no-tabs.t view on Meta::CPAN
use Test::More 0.88;
use Test::NoTabs;
my @files = (
    'lib/Finance/Calendar.pm',
    'lib/Finance/Calendar.pod',
    't/00-check-deps.t',
    't/00-compile.t',
    't/00-report-prereqs.dd',
    't/00-report-prereqs.t',
    't/calendar.t',
    't/rc/perlcriticrc',
    't/rc/perltidyrc',
    't/tactical_exchanges.t',
    'xt/author/critic.t',
    'xt/author/distmeta.t',
    'xt/author/eol.t',
    'xt/author/minimum-version.t',
    'xt/author/mojibake.t',
    'xt/author/no-tabs.t',
    'xt/author/pod-syntax.t',