Finance-Calendar

 view release on metacpan or  search on metacpan

MANIFEST  view on Meta::CPAN

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

META.json  view on Meta::CPAN

{
   "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",

META.yml  view on Meta::CPAN

---
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,

README  view on Meta::CPAN

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)],
                },

README  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);
        ...

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);

README  view on Meta::CPAN


    ->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

README  view on Meta::CPAN

    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',



( run in 0.763 second using v1.01-cache-2.11-cpan-5dc5da66d9d )