App-financeta
view release on metacpan or search on metacpan
lib/App/financeta/gui.pm view on Meta::CPAN
$log->debug("Using term $term");
my $pwin = $win->{plot} || gpwin($term, size => [1024, 768, 'px']);
$win->{plot} = $pwin;
$symbol = $self->current->{symbol} unless defined $symbol;
$type = $self->current->{plot_type} unless defined $type;
my @general_plot = ();
my @volume_plot = ();
my @addon_plot = ();
my @candle_plot = ();
$self->indicator->color_idx(0); # reset color index
if (defined $indicators and scalar @$indicators) {
# ok now create a list of indicators to plot
foreach (@$indicators) {
my $iref = $_->{indicator};
my $idata = $_->{data};
my $iplot = $self->indicator->get_plot_args($data(,(0)), $idata, $iref);
next unless $iplot;
if (ref $iplot eq 'ARRAY') {
push @general_plot, @$iplot if scalar @$iplot;
} elsif (ref $iplot eq 'HASH') {
my $iplot_gen = $iplot->{general};
push @general_plot, @$iplot_gen if $iplot_gen and scalar @$iplot_gen;
my $iplot_vol = $iplot->{volume};
push @volume_plot, @$iplot_vol if $iplot_vol and scalar @$iplot_vol;
my $iplot_addon = $iplot->{additional};
push @addon_plot, @$iplot_addon if $iplot_addon and scalar @$iplot_addon;
my $iplot_cdl = $iplot->{candle};
push @candle_plot, @$iplot_cdl if $iplot_cdl and scalar @$iplot_cdl;
} else {
$log->warn('Unable to handle plot arguments in ' . ref($iplot) . ' form!');
}
}
}
if (defined $buysell and ref $buysell eq 'HASH' and
defined $buysell->{buys} and defined $buysell->{sells}) {
my $buys = $buysell->{buys};
my $sells = $buysell->{sells};
my $rtpnl = $buysell->{rtpnl};
if (ref $buys eq 'PDL' and ref $sells eq 'PDL') {
my $bsplot = $self->indicator->get_plot_args_buysell(
$data(,(0)), $buys, $sells, $rtpnl);
if (defined $bsplot and ref $bsplot eq 'ARRAY') {
push @general_plot, @$bsplot if scalar @$bsplot;
} elsif (ref $bsplot eq 'HASH') {
my $bsplot_gen = $bsplot->{general};
if ($bsplot_gen) {
push @general_plot, @$bsplot_gen if scalar @$bsplot_gen;
}
my $bsplot_addon = $bsplot->{additional};
if (ref $bsplot_addon eq 'ARRAY' and scalar (@$bsplot_addon)) {
push @addon_plot, @$bsplot_addon;
}
}
} else {
$log->warn("Unable to plot invalid buy-sell data");
}
}
$pwin->reset();
# use multiplot
$pwin->multiplot();
my %binmode = ();
if ($^O !~ /Win32/ and $Alien::Gnuplot::version < 4.6) {
$log->debug("Binary mode is set to 0 due to gnuplot $Alien::Gnuplot::version");
$binmode{binary} = 0;
}
if ($type eq 'OHLC') {
my %addon_gen = ();
if (@addon_plot) {
$addon_gen{size} = ["1, 0.7"];
$addon_gen{origin} = [0, 0.3];
$addon_gen{bmargin} = 0;
$addon_gen{lmargin} = 9;
$addon_gen{rmargin} = 2;
}
$pwin->plot({
%binmode,
object => '1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb "black" behind',
title => ["$symbol Open-High-Low-Close", textcolor => 'rgb "white"'],
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
xlabel => ['Date', textcolor => 'rgb "yellow"'],
ylabel => ['Price', textcolor => 'rgb "yellow"'],
xdata => 'time',
xtics => {format => '%Y-%m-%d', rotate => -90, textcolor => 'orange', },
ytics => {textcolor => 'orange'},
label => [1, $self->brand, textcolor => 'rgb "cyan"', at => "graph 0.90,0.03"],
%addon_gen,
},
{
with => 'financebars',
linecolor => 'white',
legend => 'Price',
},
$data(,(0)), $data(,(1)), $data(,(2)), $data(,(3)), $data(,(4)),
@general_plot,
);
if (@addon_plot) {
$pwin->plot({
%binmode,
object => '1',
title => '',
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
ylabel => '',
xlabel => '',
xtics => '',
ytics => {textcolor => 'orange'},
bmargin => 0,
tmargin => 0,
lmargin => 9,
rmargin => 2,
size => ["1,0.3"], #bug in P:G:G
origin => [0, 0],
label => [1, "", at => "graph 0.90,0.03"],
},
@addon_plot,
);
}
} elsif ($type eq 'OHLCV') {
my %addon_gen = ();
my %addon_vol = ();
if (@addon_plot) {
$addon_gen{size} = ["1, 0.6"]; #bug in P:G:G
$addon_gen{origin} = [0, 0.4];
$addon_vol{size} = ["1, 0.2"]; #bug in P:G:G
$addon_vol{origin} = [0, 0];
} else {
$addon_gen{size} = ["1, 0.7"]; #bug in P:G:G
$addon_gen{origin} = [0, 0.3];
$addon_vol{size} = ["1, 0.3"]; #bug in P:G:G
$addon_vol{origin} = [0, 0];
$addon_vol{object} = '1'; # needed as otherwise the addon plot does it
}
$pwin->plot({
%binmode,
object => '1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb "black" behind',
xlabel => ['Date', textcolor => 'rgb "yellow"'],
ylabel => ['Price', textcolor => 'rgb "yellow"'],
title => ["$symbol Price & Volume", textcolor => "rgb 'white'"],
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
xdata => 'time',
xtics => {format => '%Y-%m-%d', rotate => -90, textcolor => 'orange', },
ytics => {textcolor => 'orange'},
bmargin => 0,
lmargin => 9,
rmargin => 2,
%addon_gen,
label => [1, $self->brand, textcolor => 'rgb "cyan"', at => "graph 0.90,0.03"],
},
{
with => 'financebars',
linecolor => 'white',
legend => 'Price',
},
$data(,(0)), $data(,(1)), $data(,(2)), $data(,(3)), $data(,(4)),
@general_plot,
);
if (@addon_plot) {
$pwin->plot({
%binmode,
object => '1',
title => '',
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
ylabel => '',
xlabel => '',
xtics => '',
ytics => {textcolor => 'orange'},
bmargin => 0,
tmargin => 0,
lmargin => 9,
rmargin => 2,
size => ["1,0.2"], #bug in P:G:G
origin => [0, 0.2],
label => [1, "", at => "graph 0.90,0.03"],
},
@addon_plot,
);
}
$pwin->plot({
%binmode,
title => '',
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
ylabel => ['Volume (in 1M)', textcolor => 'rgb "yellow"'],
xlabel => '',
xtics => '',
ytics => {textcolor => 'orange'},
bmargin => 0,
tmargin => 0,
lmargin => 9,
rmargin => 2,
%addon_vol,
label => [1, "", at => "graph 0.90,0.03"],
},
{
with => 'impulses',
legend => 'Volume',
linecolor => 'cyan',
},
$data(,(0)), $data(,(5)) / 1e6,
@volume_plot,
);
} elsif ($type eq 'CANDLE') {
my %addon_gen = ();
if (@addon_plot) {
$addon_gen{size} = ["1, 0.7"];
$addon_gen{origin} = [0, 0.3];
$addon_gen{bmargin} = 0;
$addon_gen{lmargin} = 9;
$addon_gen{rmargin} = 2;
}
# use candlesticks feature of Gnuplot
$pwin->plot({
%binmode,
object => '1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb "black" behind',
title => ["$symbol Open-High-Low-Close", textcolor => 'rgb "white"'],
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
xlabel => ['Date', textcolor => 'rgb "yellow"'],
ylabel => ['Price', textcolor => 'rgb "yellow"'],
ytics => {textcolor => 'orange'},
xdata => 'time',
xtics => {format => '%Y-%m-%d', rotate => -90, textcolor => 'orange', },
label => [1, $self->brand, textcolor => 'rgb "cyan"', at => "graph 0.90,0.03"],
%addon_gen,
},
{
with => 'candlesticks',
linecolor => 'white',
legend => 'Price',
},
$data(,(0)), $data(,(1)), $data(,(2)), $data(,(3)), $data(,(4)),
@general_plot,
@candle_plot,
);
if (@addon_plot) {
$pwin->plot({
%binmode,
object => '1',
title => '',
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
ylabel => '',
xlabel => '',
xtics => '',
ytics => {textcolor => 'orange'},
bmargin => 0,
tmargin => 0,
lmargin => 9,
rmargin => 2,
size => ["1,0.3"], #bug in P:G:G
origin => [0, 0],
label => [1, "", at => "graph 0.90,0.03"],
},
@addon_plot,
);
}
} elsif ($type eq 'CANDLEV') {
my %addon_gen = ();
my %addon_vol = ();
if (@addon_plot) {
$addon_gen{size} = ["1, 0.6"]; #bug in P:G:G
$addon_gen{origin} = [0, 0.4];
$addon_vol{size} = ["1, 0.2"]; #bug in P:G:G
$addon_vol{origin} = [0, 0];
} else {
$addon_gen{size} = ["1, 0.7"]; #bug in P:G:G
$addon_gen{origin} = [0, 0.3];
$addon_vol{size} = ["1, 0.3"]; #bug in P:G:G
$addon_vol{origin} = [0, 0];
$addon_vol{object} = '1'; # needed as otherwise the addon plot does it
}
$pwin->plot({
%binmode,
object => '1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb "black" behind',
title => ["$symbol Price & Volume", textcolor => "rgb 'white'"],
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
xlabel => ['Date', textcolor => 'rgb "yellow"'],
ylabel => ['Price', textcolor => 'rgb "yellow"'],
xdata => 'time',
ytics => {textcolor => 'orange'},
xtics => {format => '%Y-%m-%d', rotate => -90, textcolor => 'orange', },
tmargin => '',
bmargin => 0,
lmargin => 9,
rmargin => 2,
%addon_gen,
label => [1, $self->brand, textcolor => 'rgb "cyan"', at => "graph 0.90,0.03"],
},
{
with => 'candlesticks',
linecolor => 'white',
legend => 'Price',
},
$data(,(0)), $data(,(1)), $data(,(2)), $data(,(3)), $data(,(4)),
@general_plot,
@candle_plot,
);
if (@addon_plot) {
$pwin->plot({
%binmode,
object => '1',
title => '',
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
ylabel => '',
xlabel => '',
xtics => '',
ytics => {textcolor => 'orange'},
bmargin => 0,
tmargin => 0,
lmargin => 9,
rmargin => 2,
size => ["1,0.2"], #bug in P:G:G
origin => [0, 0.2],
label => [1, "", at => "graph 0.90,0.03"],
},
@addon_plot,
);
}
$pwin->plot({
%binmode,
title => '',
ylabel => ['Volume (in 1M)', textcolor => 'rgb "yellow"'],
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
xtics => '',
xlabel => '',
ytics => {textcolor => 'orange'},
bmargin => 0,
tmargin => 0,
lmargin => 9,
rmargin => 2,
%addon_vol,
label => [1, "", at => "graph 0.90,0.03"],
},
{
with => 'impulses',
legend => 'Volume',
linecolor => 'cyan',
},
$data(,(0)), $data(,(5)) / 1e6,
@volume_plot,
);
} elsif ($type eq 'CLOSEV') {
my %addon_gen = ();
my %addon_vol = ();
if (@addon_plot) {
$addon_gen{size} = ["1, 0.6"]; #bug in P:G:G
$addon_gen{origin} = [0, 0.4];
$addon_vol{size} = ["1, 0.2"]; #bug in P:G:G
$addon_vol{origin} = [0, 0];
} else {
$addon_gen{size} = ["1, 0.7"]; #bug in P:G:G
$addon_gen{origin} = [0, 0.3];
$addon_vol{size} = ["1, 0.3"]; #bug in P:G:G
$addon_vol{origin} = [0, 0];
$addon_vol{object} = '1'; # needed as otherwise the addon plot does it
}
$pwin->plot({
%binmode,
object => '1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb "black" behind',
title => ["$symbol Price & Volume", textcolor => "rgb 'white'"],
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
ylabel => ['Close Price', textcolor => 'rgb "yellow"'],
xlabel => ['Date', textcolor => 'rgb "yellow"'],
xdata => 'time',
xtics => {format => '%Y-%m-%d', rotate => -90, textcolor => 'orange', },
ytics => {textcolor => 'orange'},
bmargin => 0,
lmargin => 9,
rmargin => 2,
%addon_gen,
label => [1, $self->brand, textcolor => 'rgb "cyan"', at => "graph 0.90,0.03"],
},
{
with => 'lines',
linecolor => 'white',
legend => 'Close Price',
},
$data(,(0)), $data(,(4)),
@general_plot,
);
if (@addon_plot) {
$pwin->plot({
%binmode,
object => '1',
title => '',
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
ylabel => '',
xlabel => '',
xtics => '',
ytics => {textcolor => 'orange'},
bmargin => 0,
tmargin => 0,
lmargin => 9,
rmargin => 2,
size => ["1,0.2"], #bug in P:G:G
origin => [0, 0.2],
label => [1, "", at => "graph 0.90,0.03"],
},
@addon_plot,
);
}
$pwin->plot({
%binmode,
title => '',
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
ylabel => ['Volume (in 1M)', textcolor => 'rgb "yellow"'],
xlabel => '',
xtics => '',
ytics => {textcolor => 'orange'},
bmargin => 0,
tmargin => 0,
lmargin => 9,
rmargin => 2,
%addon_vol,
label => [1, "", at => "graph 0.90,0.03"],
},
{
with => 'impulses',
legend => 'Volume',
linecolor => 'cyan',
},
$data(,(0)), $data(,(5)) / 1e6,
@volume_plot,
);
} else {
$type = 'CLOSE';
my %addon_gen = ();
if (@addon_plot) {
$addon_gen{size} = ["1, 0.7"];
$addon_gen{origin} = [0, 0.3];
$addon_gen{bmargin} = 0;
$addon_gen{lmargin} = 9;
$addon_gen{rmargin} = 2;
}
$pwin->plot({
%binmode,
object => '1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb "black" behind',
title => ["$symbol Close Price", textcolor => 'rgb "white"'],
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
xlabel => ['Date', textcolor => 'rgb "yellow"'],
ylabel => ['Close Price', textcolor => 'rgb "yellow"'],
xdata => 'time',
xtics => {format => '%Y-%m-%d', rotate => -90, textcolor => 'orange', },
ytics => {textcolor => 'orange'},
label => [1, $self->brand, textcolor => 'rgb "cyan"', at => "graph 0.90,0.03"],
%addon_gen,
},
{
with => 'lines',
linecolor => 'white',
legend => 'Close Price',
},
$data(,(0)), $data(,(4)),
@general_plot,
);
if (@addon_plot) {
$pwin->plot({
%binmode,
object => '1',
title => '',
key => ['on', 'outside', textcolor => 'rgb "yellow"'],
border => 'linecolor rgbcolor "white"',
ylabel => '',
xlabel => '',
xtics => '',
ytics => {textcolor => 'orange'},
bmargin => 0,
tmargin => 0,
lmargin => 9,
rmargin => 2,
size => ["1,0.3"], #bug in P:G:G
origin => [0, 0],
label => [1, "", at => "graph 0.90,0.03"],
},
@addon_plot,
);
}
}
$pwin->end_multi;
# make the current plot type the type
$self->current->{plot_type} = $type if defined $type;
}
sub plot_data_highcharts {
my ($self, $win, $data, $symbol, $type, $indicators, $buysell) = @_;
return unless defined $data;
$symbol = $self->current->{symbol} unless defined $symbol;
$type = $self->current->{plot_type} unless defined $type;
### for OHLC type
## timestamp: data(,(0))
## OHLC: data(, (1)), data(, (2)), data(,(3)), data(,(4))
## Volume: data(, (5))
$log->debug("Symbol $symbol. Plot type $type");
### we use Template toolkit to take a template, plug data in via variables
### and auto-generate an HTML page that will then render graphics as needed
### maybe all the data should be in the HTML form for easy generation
### and the JS code has to be fixed and pre-written.
### so the JS code uses variables that are defined in the HTML data that is printed by
### the template
my $ttfile = get_file_path('chart.tt');
$log->info("Template file path: $ttfile");
my $html = File::Spec->catfile($self->tmpdir, lc "$symbol\_$type.html");
## highcharts requires timestamp in milliseconds
## transpose the data for JSON charting
my $chart_type_pretty;
my @charts = ();
$type //= 'OHLC';
my $next_y_axis = 0;
my $has_volume_chart = 0;
if ($type eq 'OHLC' or $type eq 'CANDLE') {
$chart_type_pretty = ($type eq 'CANDLE') ? 'Candlestick' : 'OHLC Price';
my $ppdl = encode_json pdl($data(,(0)) * 1000, $data(,(1)), $data(,(2)), $data(,(3)), $data(,(4)))->transpose->unpdl;
$log->debug($ppdl);
push @charts, {
title => $symbol,
data => $ppdl,
type => ($type eq 'CANDLE') ? 'candlestick' : 'ohlc',
id => lc "$symbol-$type",
lib/App/financeta/gui.pm view on Meta::CPAN
title => "Buy Signals",
data => $bpdljs,
type => 'line',
id => lc "$symbol-buy-signals",
y_axis => 0,
is_signal => 1,
marker_symbol => 'triangle',
marker_color => 'green',
};
my $spdl = pdl($data(,(0)) * 1000, $sells)->transpose;
my $sidx = $spdl((1))->which;#check if !0 is true
my $spdlclean = $spdl->dice_axis(1, $sidx);
$log->debug($spdlclean);
my $spdljs = encode_json $spdlclean->unpdl;
push @charts, {
title => "Sell Signals",
data => $spdljs,
type => 'line',
id => lc "$symbol-sell-signals",
y_axis => 0,
is_signal => 1,
marker_symbol => 'triangle-down',
marker_color => 'red',
};
} else {
$log->warn("Unable to plot invalid buy-sell data");
}
if (ref $buysell->{rtpnl} eq 'PDL') {
my $rpdl = pdl($data(, (0)) * 1000, $buysell->{rtpnl})->transpose;
my $rpdljs = encode_json $rpdl->unpdl;
push @charts, {
title => "Runtime P&L",
data => $rpdljs,
type => 'area',
id => lc "$symbol-runtime-pnl",
y_axis => $next_y_axis + 1,
};
}
}
my %y_axes_index = ();
foreach (@charts) {
$y_axes_index{$_->{y_axis}}++;
}
my $cheight = 400;
foreach (keys %y_axes_index) {
$cheight += 200;
}
my $ttconf = {
page => {
title => "$chart_type_pretty plot of $symbol",
close_on_idle => 1,
},
chart => {
height => $cheight . "px",
yaxes_index => [sort keys(%y_axes_index)],
charts => \@charts,
title => $symbol,
},
};
my $tt = Template->new({ ABSOLUTE => 1 });
my $ret = $tt->process($ttfile, $ttconf, $html, { binmode => ':utf8' });
if ($ret) {
my $url = "file://$html";
my $ok = Browser::Open::open_browser($url, 1);
if (not defined $ok) {
message("Error finding a browser to open $url");
$log->warn("Error finding a default browser to open $url");
} elsif ($ok != 0) {
message("Error opening $url");
$log->warn("Error opening $url in default browser");
}
} else {
$log->error("Failed to generate $html from $ttfile: " . $tt->error() . "\n");
message("Failed to plot chart in a browser!");
}
# make the current plot type the type
$self->current->{plot_type} = $type if defined $type;
}
1;
__END__
### COPYRIGHT: 2013-2025. Vikas N. Kumar. All Rights Reserved.
### AUTHOR: Vikas N Kumar <vikas@cpan.org>
### DATE: 3rd Jan 2014
### LICENSE: Refer LICENSE file
=head1 NAME
App::financeta::gui
=head1 SYNOPSIS
App::financeta::gui is a perl module allowing the user to perform technical
analysis on financial data stored as PDLs. It is the basis of the graphics
application L<App::financeta> which can be used by users to do financial stocks
research with Technical Analysis.
=head1 VERSION
0.16
=head1 METHODS
=over
=item B<new>
Creates a new instance of C<App::financeta::gui>. Takes in various properties that
the user might want to override. Check the B<PROPERTIES> section to view the
different properties.
=item B<run>
This function starts the graphical user interface (GUI) and uses
L<POE::Loop::Prima> and L<Prima> to do all its work. This is our current choice
of the GUI framework but it need not be in the future.
=back
=head1 PROPERTIES
( run in 1.437 second using v1.01-cache-2.11-cpan-5735350b133 )