[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GT] SVN Commit r642 - branches/exp/Scripts
Author: thomas
Date: 2008-06-18 05:51:55 +0200 (Wed, 18 Jun 2008)
New Revision: 642
Modified:
branches/exp/Scripts/manage_portfolio.pl
Log:
Corrected documentation. Allowed buy and sell as alternatives to bought and sold throughout. Eliminated warning messages on input errors and corrected input errors on those occasions or abort. Use CLOSE if no price is given. Allow OPEN, CLOSE, HIGH, and LOW as prices. Implement report analysis.
Modified: branches/exp/Scripts/manage_portfolio.pl
===================================================================
--- branches/exp/Scripts/manage_portfolio.pl 2008-06-17 02:47:51 UTC (rev 641)
+++ branches/exp/Scripts/manage_portfolio.pl 2008-06-18 03:51:55 UTC (rev 642)
@@ -25,6 +25,8 @@
use Getopt::Long;
use Time::localtime;
use File::Spec;
+use GT::Tools qw(:timeframe);
+use GT::Indicators::MaxDrawDown;
use Pod::Usage;
use Cwd;
@@ -43,7 +45,7 @@
./manage_portfolio.pl <portfolio> bought <quantity> <share> \
<price> [ <date> <source> ]
./manage_portfolio.pl <portfolio> sold <quantity> <share> \
- <price> [ <date> <source> ]
+ [ <price> <date> <source> ]
./manage_portfolio.pl <portfolio> stop <share> <price>
./manage_portfolio.pl <portfolio> set initial-sum <sum of money>
./manage_portfolio.pl <portfolio> set broker <broker>
@@ -59,12 +61,16 @@
=item * C<< <portfolio> >> is the filename of portfolio to use. it can
be a non-existent file, in which case it will be created
-=item * C<< <quantity> <price> >> are numeric values for C<< <share>
->> which is the appropriate stock symbol or cusip or other identifier
+=item * C<< <quantity> >> is a numeric value for C<< <share> >>
+which is the appropriate stock symbol or cusip or other identifier
+=item * C<< <price> >> is either a numeric values for C<< <share> >>,
+or it may be one of OPEN, CLOSE, LOW, or HIGH. If C<< <price> >> is
+omitted, the CLOSE is chosen.
+
=item * C<< <date> >> is optional, the date the transaction happened on. if not
- supplied the default value for 'today' will be supplied. preferred GT
- format for dates is 'YYYY-MM-DD'.
+supplied the default value for 'today' will be supplied. The
+format for dates is 'YYYY-MM-DD'.
=item * C<< <source> >> is optional, a text string, it can be used to
note the source of the stock transaction. typically an internal GT
@@ -223,7 +229,10 @@
=over
-=item * Needs to do a better job of checking input values for bought/sold operations or needs to provide a way of completely removing a bad entry
+=item * Needs to do a better job of checking input values for bought/sold
+operations or needs to provide a way of completely removing a bad entry.
+For example, if you mistype a quantity as some text, one can no longer remove
+that entry.
=item * Might also be nice to provide a couple of output modes; say
one to generate a file that can used as input for the
@@ -322,6 +331,7 @@
}
} elsif ( ($cmd eq "bought") or ($cmd eq "sold")
+ or ($cmd eq "buy") or ($cmd eq "sell")
or ($cmd eq "file") or ($cmd eq "db")) {
$changes = 1;
@@ -369,6 +379,8 @@
# populate @orderlist from stuff read from db portfolio
# after a bit of massage and careful kneeding
foreach $batchorder (@portlist) {
+ next if ( $batchorder =~ m/^\s*$/ );
+ next if ( $batchorder =~ m/^\s*#/ );
# separate lines into list
# prepend "bought" or "sold" depending on qty (postive or negative)
# ensure $source present even if ""
@@ -376,30 +388,27 @@
my ( $quantity, $code, $price, $date, $source ) = split /:/, $batchorder;
$source = "" if ( ! defined($source) );
( $quantity > 0 ) ? ( $trans = "bought" ) : ( $trans = "sold" );
- push @orderlist, "$trans $quantity $code $price $date $source";
+ push @orderlist, [$trans, $quantity, $code, $price, $date, $source];
}
} else {
# no batch file, place the command line parameters into the
# start of the data array
- #my ($quantity, $code, $price, $date) = @ARGV;
my ($quantity, $code, $price, $date, $source) = @ARGV;
- # for this one off we have to insert the $cmd (bought or sold)
- #$orderlist[0]="$cmd $quantity $code $price $date";
- $orderlist[0]="$cmd $quantity $code $price $date $source";
+ $source = "" if ( ! defined($source) );
+ $orderlist[0]=[$cmd, $quantity, $code, $price, $date, $source];
}
foreach $batchorder (@orderlist) {
- chomp($batchorder);
- next if ( $batchorder =~ m/^\s*$/ );
- next if ( $batchorder =~ m/^\s*#/ );
-# my ($cmd, $quantity, $code, $price, $date) = split(/ /,$batchorder);
my ($cmd, $quantity, $code, $price, $date, $source) =
- split(/\s+/,$batchorder);
-print STDERR "batch line\n"
- . "cmd=$cmd, quantity=$quantity, code=$code, price=$price,"
- . " date=$date, source=$source\n" if ( $verbose > 1 );
+ @{$batchorder};
+#print STDERR "batch line\n"
+# . "cmd=$cmd, quantity=$quantity, code=$code, price=$price,"
+# . " date=$date, source=$source\n" if ( $verbose > 1 );
+ if (! defined($code)) {
+ die "No code given to act on. No calculations done.\n"
+ }
if (! defined($date)) {
$date = sprintf("%04d-%02d-%02d",
localtime->year + 1900, localtime->mon + 1, localtime->mday);
@@ -407,18 +416,43 @@
if ( $date =~ m@/@ ) {
# fix me
# hack in date conversion -- i like date::manip
- die "come on -- gt only likes dates formatted yyyy-mm-dd\n"
- . "i'm not going accept \"$date\", nor will i convert it, sorry (not).\n";
+ die "Illegal date format for $date.\nThis script accepts only dates formatted yyyy-mm-dd\n";
}
+ my $what;
+ if ( defined($price) ) {
+ $what = $CLOSE if $price eq 'CLOSE';
+ $what = $OPEN if $price eq 'OPEN';
+ $what = $LOW if $price eq 'LOW';
+ $what = $HIGH if $price eq 'HIGH';
+ undef($price) if defined($what);
+ }
+ if (! defined($price)) {
+ $what = $CLOSE unless $what;
+ my $db = create_db_object();
+ my $prices;
+ if (! $db->has_code($code)) {
+ die "Code not available in database. No calculations done.\n\n";
+ }
+ eval {
+ $prices = $db->get_last_prices($code, 1);
+ };
+ if ($@) {
+ die "Error while retrieving prices. No calculations done.\n\n";
+ }
+ $date = $prices->find_nearest_preceding_date($date);
+ $price = $prices->at_date($date)->[$what];
+ if (! defined($price)) {
+ die "Price for $date not available in database. No calculations done.\n\n";
+ }
+ }
my $order = GT::Portfolio::Order->new;
-# if ($cmd eq "bought") {
if ( $cmd =~ m/bought|buy/i ) {
$order->set_buy_order;
} elsif ( $cmd =~ m/sold|sell/i ) {
$order->set_sell_order;
} else {
- die "sorry -- i don't recognize $cmd in \"$batchorder\"\n";
+ die "$cmd not recognized in \"$batchorder\"\n";
}
$order->set_type_limited;
$order->set_price($price);
@@ -452,9 +486,12 @@
print " done.\n";
# store the portfolio evaluation for the given date
- print "eval portfolio for $date";
- $pf->store_evaluation($date);
- print " done.\n";
+# This requires to actually perform the evaluation when applying orders
+# or run through it now, as in "report pos" but there seems to be
+# no purpose for buy/sell.
+# print "eval portfolio for $date";
+# $pf->store_evaluation($date);
+# print " done.\n";
}
} elsif ($cmd eq "stop") {
@@ -573,7 +610,6 @@
if ($template eq '');
} else {
$template = '';
-print STDERR "not using template\n";
}
my $db = create_db_object();
@@ -589,53 +625,47 @@
GT::Report::OpenPositions($pf, $detailed);
} elsif ( $what eq "historic" || $what =~ m/hi.*/i ) {
- GT::Report::Portfolio($pf, $detailed)
- || print "\ndidn't find any history in the portfolio\n";
+ GT::Report::Portfolio($pf, $detailed);
} elsif ( $what eq "analysis" || $what =~ m/an.*/i ) {
- print "\n$0: sorry analysis not yet implemented\n";
- print "\nhumm, well, not so fast there grasshopper\n"
- . "-- here we have hacked in a call to\n"
- . "GT::Report::SimplePortfolioAnalysis(...)\n\n";
- GT::Report::SimplePortfolioAnalysis($pf->real_global_analysis);
- print "\n\nbut as you can see it don't do very much\n";
+ my ($sec, $min, $hour, $d, $m, $y, $wd, $yd) = @{localtime()};
+ my $today = sprintf("%04d-%02d-%02d", $y+1900, $m+1, $d);
+ # set some of the analysis information as in GT/Backtest.pm
+ # following line 127 (after comment "launch the analysis")
+ # should be done when applying positions below
+ my $a = $pf->real_global_analysis;
+ # Buy and hold for a whole portfolio could be against a benchmark
+ # Examine all positions for earliest and latest dates
+ $a->{'first_date'} = $pf->{'history'}->[0]->{'open_date'};
+ $a->{'last_date'} = $today;
+ $a->{'duration'} = (GT::DateTime::map_date_to_time($timeframe, $a->{'last_date'}) - GT::DateTime::map_date_to_time($timeframe, $a->{'first_date'})) / 31557600;
+ # From Backtest.pm, "Launch the analysis"
+ my $buyhold = 0;
+ my $buyhold_name = GT::Conf::get("Analysis::BuyAndHold")||'SPY';
+ my ($calc, $first, $last) = find_calculator($db, $buyhold_name, $DAY, 0, $a->{'first_date'}, $a->{last_date});
- print "\ntrial and error poking on this stuff suggests that\n";
- print "a portfolio cannot be analyzed without a history, and\n";
- print "you ain't got no history unless a position is closed.\n";
- print "seems odd to me, but that methinks is the way it works.\n";
+ $buyhold = $calc->prices->at($last)->[$LAST] /
+ $calc->prices->at($first)->[$LAST] - 1 if ( $calc->prices->at($first)->[$LAST] != 0);
+ $a->{'buyandhold'} = $buyhold;
+ # Standardized performance
+ GT::Conf::default("Analysis::ReferenceTimeFrame", "year");
+ my $tf_name = GT::Conf::get("Analysis::ReferenceTimeFrame");
+ my $ref_tf = GT::DateTime::name_to_timeframe($tf_name);
+ my $exp = GT::DateTime::timeframe_ratio($ref_tf, $timeframe)
+ / ($last - $first + 1);
+ $a->{'std_buyandhold'} = (($buyhold + 1) ** $exp) - 1;
+ $a->{'std_performance'} = (($a->{'performance'} + 1) ** $exp) - 1;
+ $a->{'std_timeframe'} = $tf_name;
+ # Calculate Buy & Hold Max Draw Down with our MaxDrawDown Indicator
+ my $indicator_maxdd = GT::Indicators::MaxDrawDown->new();
+ $indicator_maxdd->calculate($calc, $last);
+ my $buyandhold_maxdd = $calc->indicators->get($indicator_maxdd->get_name, $last);
+ if (defined($buyandhold_maxdd)) {
+ $a->{'buyandhold_max_draw_down'} = $buyandhold_maxdd;
+ }
- my ($sec, $min, $hour, $d, $m, $y, $wd, $yd) = localtime();
- my $today = sprintf("%04d-%02d-%02d", $y+1900, $m+1, $d);
- if ( $pf->has_historic_evaluation("$today") ) {
- GT::Report::SimplePortfolioAnalysis($pf->real_global_analysis);
- # following one divides by zero, without a history in portfolio
- GT::Report::PortfolioAnalysis($pf->real_global_analysis, $detailed);
- } else {
- print "\ndon't see no stinkin' history in your portfolio man!\n";
- print "you got not stinkin' history, you get no stinkin' analysis!\n";
- print "\n\n";
- print "on the other hand we could be nice and artifically close\n";
- print "everthing today, run the analyses and leave, but that's\n";
- print "just too bloody polite, if you ask me!\n";
- print "\nif you're interested see the bit near the comment\n";
- print "# Close the open positions in sub backtest_single ../GT/BackTest.pm\n";
- print "\n\n";
-
- print "\n\n";
- }
+ GT::Report::PortfolioAnalysis($a, $detailed);
-# # lets try GT::Report::PortfolioAnalysis($pf, $detailed)
-# print "\nwell, ok -- here we have hacked in a call to\n"
-# . "GT::Report::PortfolioAnalysis($pf->real_global_analysis, $detailed)\n\n";
-# GT::Report::PortfolioAnalysis($pf->real_global_analysis, $detailed);
-# print "\n\nbut as you can see it don't work so good!\n";
-# print "\n\n---\n\n";
-#
-# print "\n\n real_analysis_by_code T \n\n";
-# GT::Report::PortfolioAnalysis($pf->real_analysis_by_code, "T");
-# print "\n\n---\n\n";
-
} else {
print "\n$0: unknown report type \"$what\"\n";
}
@@ -690,7 +720,7 @@
if ( ( mv "$pfname", "$pfname.$ext" ) == 1 ) {
print "ok file name is $pfname.$ext ";
}else{
- print "\noh no! the rename failed. $!\n";
+ print "\nCould not create backup file. $!\n";
}
}
$pf->store($pfname);