X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?a=blobdiff_plain;f=yarrg%2Fweb%2Froutetrade;h=397e3854372787170afe7e1861de55150aa6495a;hb=202510fa47a7459760d6e41f6017500696e344d8;hp=b394a3527843e0cdeddd0ca7e7d1376506a45f3b;hpb=0191640f64abf2adf416dbd2fcc6632ff58e8e8f;p=ypp-sc-tools.db-live.git diff --git a/yarrg/web/routetrade b/yarrg/web/routetrade index b394a35..397e385 100644 --- a/yarrg/web/routetrade +++ b/yarrg/web/routetrade @@ -38,6 +38,9 @@ $dbh @islandids @archipelagoes $qa +$max_mass +$max_volume +$lossperleaguepct %args> <&| script &> da_pageload= Date.now(); @@ -45,11 +48,13 @@ $qa <%perl> +my $loss_per_league= defined $lossperleaguepct ? $lossperleaguepct*0.01 : 1e-7; + my $now= time; -my $loss_per_league= 1e-7; my @flow_conds; my @query_params; +my %dists; my $sd_condition= sub { my ($bs, $ix) = @_; @@ -85,10 +90,12 @@ foreach my $src_i (0..$#islandids) { if ($specific && !$confusing && # With a circular route, do not carry goods round the loop - !($src_i==0 && $dst_i==$#islandids && + !(($src_i==0 || $src_i==$#islandids) && + $dst_i==$#islandids && $src_isle == $islandids[$dst_i])) { if ($islandpair{$src_isle,$dst_isle}) { $confusing= 1; +print "confusing $src_i $src_isle $dst_i $dst_isle\n"; } else { $islandpair{$src_isle,$dst_isle}= [ $src_i, $dst_i ]; @@ -159,6 +166,20 @@ my $sth= $dbh->prepare($stmt); $sth->execute(@query_params); my @flows; +my $distquery= $dbh->prepare(" + SELECT dist FROM dists WHERE aiid = ? AND biid = ? + "); +my $distance= sub { + my ($from,$to)= @_; + my $d= $dists{$from}{$to}; + return $d if defined $d; + $distquery->execute($from,$to); + $d = $distquery->fetchrow_array(); + defined $d or die "$from $to ?"; + $dists{$from}{$to}= $d; + return $d; +}; + my @cols= ({ NoSort => 1 }); my $addcols= sub { @@ -188,9 +209,12 @@ $addcols->({ DoReverse => 1, SortColKey => 'MarginSortKey' }, qw( Margin )); $addcols->({ DoReverse => 1 }, - qw( unitprofit MaxQty - MaxCapital MaxProfit + qw( unitprofit MaxQty MaxCapital MaxProfit dist )); +foreach my $v (qw(MaxMass MaxVolume)) { + $addcols->({ + DoReverse => 1, Total => 0, SortColKey => "${v}SortKey" }, $v); +} %perl> @@ -239,6 +263,12 @@ $addcols->({ DoReverse => 1 }, % } <%perl> + +if (!@flows) { + print 'No profitable trading opportunities were found.'; + return; +} + foreach my $f (@flows) { $f->{MaxQty}= $f->{'org_qty_agg'} < $f->{'dst_qty_agg'} @@ -246,6 +276,12 @@ foreach my $f (@flows) { $f->{MaxProfit}= $f->{MaxQty} * $f->{'unitprofit'}; $f->{MaxCapital}= $f->{MaxQty} * $f->{'org_price'}; + $f->{MaxMassSortKey}= $f->{MaxQty} * $f->{'unitmass'}; + $f->{MaxVolumeSortKey}= $f->{MaxQty} * $f->{'unitvolume'}; + foreach my $v (qw(Mass Volume)) { + $f->{"Max$v"}= sprintf "%.1f", $f->{"Max${v}SortKey"} * 1e-6; + } + $f->{MarginSortKey}= sprintf "%d", $f->{'dst_price'} * 10000 / $f->{'org_price'}; $f->{Margin}= sprintf "%3.1f%%", @@ -255,6 +291,8 @@ foreach my $f (@flows) { $f->{'dst_price'} * (1.0 - $loss_per_league) ** $f->{'dist'} - $f->{'org_price'}; + $dists{'org_id'}{'dst_id'}= $f->{'dist'}; + my @uid= $f->{commodid}; foreach my $od (qw(org dst)) { push @uid, @@ -331,7 +369,7 @@ die "$cmpu $uue ?" if length $cmpu > 20;
% if (@islandids<=1) { -Route is trivial. +Route contains only one location. % } % if (!$specific) { Route contains archipelago(es), not just specific islands. @@ -340,7 +378,7 @@ Route contains archipelago(es), not just specific islands. Route is complex - it visits the same island several times and isn't a simple loop. % } -Therefore, optimal trade pattern not calculated. +Therefore, optimal voyage trade plan not calculated. % } else { # ========== OPTMISATION ========== <%perl> @@ -349,9 +387,9 @@ my $cplex= " Maximize totalprofit: - ".(join " + + ".(join " ", map { - sprintf "%.20f %s", $_->{ExpectedUnitProfit}, $_->{Var} + sprintf "%+.20f %s", $_->{ExpectedUnitProfit}, $_->{Var} } @flows)." Subject To @@ -382,16 +420,50 @@ foreach my $flow (@flows) { foreach my $cstname (sort keys %avail_csts) { my $c= $avail_csts{$cstname}; $cplex .= " - ". sprintf("%-30s","$cstname:")." ". + ". sprintf("%-30s","$cstname:")." ". join("+", @{ $c->{Flows} }). " <= ".$c->{Qty}."\n"; } +foreach my $ci (0..($#islandids-1)) { + my @rel_flows; + foreach my $f (@flows) { + next if $f->{Suppress}; + next if $f->{'org_id'} == $f->{'dst_id'}; + next unless grep { $f->{'org_id'} == $_ } + @islandids[0..$ci]; + next unless grep { $f->{'dst_id'} == $_ } + @islandids[$ci+1..@islandids-1]; + push @rel_flows, $f; +#print " RELEVANT $ci $f->{Ix} "; + } +#print " RELEVANT $ci COUNT ".scalar(@rel_flows)." "; + next unless @rel_flows; + foreach my $mv (qw(mass volume)) { + my $max_vn= "max_$mv"; + my $max= $mv eq 'mass' ? $max_mass : $max_volume; + next unless defined $max; +#print " DEFINED MAX $mv $max "; + $cplex .= " + ". sprintf("%-10s","${mv}_$ci:")." ". + join(" + ", map { ($_->{"unit$mv"}*1e-3).' f'.$_->{Ix} } @rel_flows). + " <= $max"; + } + $cplex.= "\n"; +} + $cplex.= " Bounds ".(join " ", map { "$_->{Var} >= 0" } @flows)." +"; + +$cplex.= " +Integer + ".(join " + ", map { "f$_" } (0..$#flows))." + End "; @@ -410,9 +482,11 @@ if ($qa->{'debug'}) { qw(glpsol --cpxlp /dev/stdin -o /dev/stdout)); print "
\n" if $qa->{'debug'}; my $found_section= 0; + my $glpsol_out= ''; while (<$output>) { + $glpsol_out.= $_; print encode_entities($_) if $qa->{'debug'}; - if (m/^\s*No\.\s+Column name\s+St\s+Activity\s/) { + if (m/^\s*No\.\s+Column name\s+(?:St\s+)?Activity\s/) { die if $found_section>0; $found_section= 1; next; @@ -431,11 +505,19 @@ if ($qa->{'debug'}) { $flow->{OptCapital}= $flow->{OptQty} * $flow->{'org_price'}; } print "\n" if $qa->{'debug'}; - pipethrough_run_finish($output, 'glpsol'); - die unless $found_section; + my $prerr= "\n=====\n$cplex\n=====\n$glpsol_out\n=====\n "; + pipethrough_run_finish($output,$prerr); + die $prerr unless $found_section; }; -$addcols->({ DoReverse => 1 }, qw( +$addcols->({ DoReverse => 1, Special => sub { + my ($flow,$col,$v,$spec) = @_; + if ($flow->{ExpectedUnitProfit} < 0) { + $spec->{Span}= 3; + $spec->{String}= '(Small margin)'; + $spec->{Align}= 'align=center'; + } +} }, qw( OptQty )); $addcols->({ Total => 0, DoReverse => 1 }, qw( @@ -459,6 +541,7 @@ $addcols->({ Total => 0, DoReverse => 1 }, qw(
+
% $iquery->execute($islandids[$i]);
% my ($islandname) = $iquery->fetchrow_array();
% if (!$i) {
-Start at <% $islandname |h %>
+Start at <% $islandname |h %>
% } else {
-Sail to <% $islandname |h %>
+% my $this_dist= $distance->($islandids[$i-1],$islandids[$i]);
+% $total_dist += $this_dist;
+Sail to <% $islandname |h %>
+- <% $this_dist |h %> leagues |
% }
-
<%perl>
my $age_reported= 0;
my %flowlists;
@@ -570,6 +667,11 @@ Sail to <% $islandname |h %>
next if $f->{Suppress};
next unless $f->{"${od}_id"} == $islandids[$i];
next unless $f->{OptQty};
+ my $arbitrage= $f->{'org_id'} == $f->{'dst_id'};
+ my $loop= $islandids[0] == $islandids[-1] &&
+ ($i==0 || $i==$#islandids);
+ next if $loop and ($arbitrage ? $i :
+ !!$i == !!($od eq 'org'));
my $price= $f->{"${od}_price"};
my $stallname= $f->{"${od}_stallname"};
my $todo= \$flowlists{$od}{
@@ -590,9 +692,7 @@ Sail to <% $islandname |h %>
$$todo->{Qty} += $f->{OptQty};
$$todo->{Total}= $$todo->{Price} * $$todo->{Qty};
$$todo->{Stalls}= $f->{"${od}Stalls"};
- if ($f->{'org_id'} == $f->{'dst_id'}) {
- $$todo->{"${od}Arbitrage"}= 1;
- }
+ $$todo->{"${od}Arbitrage"}= 1 if $arbitrage;
}
}
@@ -644,25 +744,32 @@ Sail to <% $islandname |h %>
% }
% };
% my $show_total= sub {
-% my ($totaldesc)= @_;
+% my ($totaldesc, $sign)= @_;
% if (defined $total) {
| <% $totaldesc %>
| <% $total |h %> total
+% $total_total += $sign * $total;
% }
% $total= undef;
% $dline= 0;
<%perl>
};
- $show_flows->('dst',0,'Deliver'); $show_total->('Proceeds');
- $show_flows->('org',1,'Collect'); $show_total->('(Arbitrage) outlay');
- $show_flows->('dst',1,'Deliver'); $show_total->('(Arbitrage) proceeds');
- $show_flows->('org',0,'Collect'); $show_total->('Outlay');
+ $show_flows->('dst',0,'Deliver'); $show_total->('Proceeds',1);
+ $show_flows->('org',1,'Collect'); $show_total->('(Arbitrage) outlay',-1);
+ $show_flows->('dst',1,'Deliver'); $show_total->('(Arbitrage) proceeds',1);
+ $show_flows->('org',0,'Collect'); $show_total->('Outlay',-1);
}
%perl>
+ | Total distance: <% $total_dist %> leagues.
+ | Overall net cash flow
+ | <%
+ $total_total < 0 ? -$total_total." loss" : $total_total." gain"
+ %>
| |