This Mason component is the core trade planner for a specific route.
+========== TODO ==========
+16:36 <ceb> alpha,byrne,papaya,turtle,jorvik,luthien is my example
+
+16:39 <ceb> Also, maybe colour to highlight the suggested trades?
+
+16:40 <ceb> columns should be sortable with the small arrows as before
+
+16:46 <ceb> Also trading plan not functional but I guess you know that :-)
+
+use POST for update. Hrrm.
+
+LATER OR NOT AT ALL
+
+adjustable potential cost of losses (rather than fixed 1e-BIG per league)
+
+max volume/mass
+
+16:38 <ceb> I don't know how hard this is, but can you show only the suggested
+ trades to start ith and have a button to show all?
+========== TODO ==========
+
</%doc>
<%args>
$dbh
</%args>
<%perl>
+my $loss_per_league= 1e-7;
+
my @flow_conds;
my @query_params;
commods.commodid commodid,
commods.unitmass unitmass,
commods.unitvolume unitvolume,
+ dist dist,
buy.price - sell.price unitprofit
FROM commods
JOIN buy on commods.commodid = buy.commodid
JOIN stalls as sell_stalls on sell.stallid = sell_stalls.stallid
JOIN stalls as buy_stalls on buy.stallid = buy_stalls.stallid
" : "")."
+ JOIN dists on aiid = sell.islandid AND biid = buy.islandid
WHERE (
".join("
OR ", @flow_conds)."
$sth->execute(@query_params);
my @flows;
-my @columns;
+my @cols;
+
+my $addcols= sub {
+ my $base= shift @_;
+ foreach my $name (@_) {
+ push @cols, { Name => $name, %$base };
+ }
+};
+
if ($qa->{ShowStalls}) {
- push @columns, qw(org_name org_stallname dst_name dst_stallname);
+ $addcols->({ Text => 1 }, qw(
+ org_name org_stallname
+ dst_name dst_stallname
+ ));
} else {
- push @columns, qw(org_name dst_name);
+ $addcols->({Text => 1 }, qw(
+ org_name dst_name
+ ));
}
-push @columns, qw(commodname
- org_price org_qty dst_price dst_qty
- unitprofit PctProfit
- MaxQty MaxCapital MaxProfit);
+$addcols->({ Text => 1 }, qw(commodname));
+$addcols->({},
+ qw( org_price org_qty dst_price dst_qty
+ Margin unitprofit MaxQty
+ MaxCapital MaxProfit
+ ));
</%perl>
</pre>
% }
-% {
<& dumptable:start, qa => $qa, sth => $sth &>
-% my $flow;
-% while ($flow= $sth->fetchrow_hashref()) {
-% $flow->{Ix}= @flows;
-% $flow->{Var}= "f$flow->{Ix}";
-% push @flows, $flow;
-<& dumptable:row, qa => $qa, sth => $sth, row => $flow &>
+% {
+% my $f;
+% while ($f= $sth->fetchrow_hashref()) {
+<%perl>
+
+ $f->{Ix}= @flows;
+ $f->{Var}= "f$f->{Ix}";
+
+ $f->{MaxQty}= $f->{'org_qty'} < $f->{'dst_qty'}
+ ? $f->{'org_qty'} : $f->{'dst_qty'};
+ $f->{MaxProfit}= $f->{MaxQty} * $f->{'unitprofit'};
+ $f->{MaxCapital}= $f->{MaxQty} * $f->{'org_price'};
+
+ $f->{Margin}= sprintf "%3.1f%%",
+ $f->{'dst_price'} * 100.0 / $f->{'org_price'} - 100.0;
+
+ $f->{"org_stallid"}= $f->{"dst_stallid"}= 'all'
+ if !$qa->{ShowStalls};
+
+ $f->{ExpectedUnitProfit}=
+ $f->{'dst_price'} * (1.0 - $loss_per_league) ** $f->{'dist'}
+ - $f->{'src_price'};
+
+ my @uid= $f->{commodid};
+ foreach my $od (qw(org dst)) {
+ push @uid,
+ $f->{"${od}_id"},
+ $f->{"${od}_price"};
+ push @uid,
+ $f->{"${od}_stallid"}
+ if $qa->{ShowStalls};
+ }
+ $f->{UidLong}= join '_', @uid;
+
+ my $base= 31;
+ my $cmpu= '';
+ map {
+ my $uue= $_;
+ my $first= $base;
+ do {
+ my $this= $uue % $base;
+print STDERR "uue=$uue this=$this ";
+ $uue -= $this;
+ $uue /= $base;
+ $this += $first;
+ $first= 0;
+ $cmpu .= chr($this + ($this < 26 ? ord('a') :
+ $this < 52 ? ord('A')-26
+ : ord('0')-52));
+print STDERR " uue=$uue this=$this cmpu=$cmpu\n";
+die "$cmpu $uue ?" if length $cmpu > 20;
+ } while ($uue);
+ $cmpu;
+ } @uid;
+ $f->{UidShort}= $cmpu;
+
+ if ($qa->{'debug'}) {
+ my @outuid;
+ $_= $f->{UidShort};
+ my $mul;
+ while (m/./) {
+ my $v= m/^[a-z]/ ? ord($&)-ord('a') :
+ m/^[A-Z]/ ? ord($&)-ord('A')+26 :
+ m/^[0-9]/ ? ord($&)-ord('0')+52 :
+ die "$_ ?";
+ if ($v >= $base) {
+ push @outuid, 0;
+ $v -= $base;
+ $mul= 1;
+#print STDERR "(next)\n";
+ }
+ die "$f->{UidShort} $_ ?" unless defined $mul;
+ $outuid[$#outuid] += $v * $mul;
+
+#print STDERR "$f->{UidShort} $_ $& v=$v mul=$mul ord()=".ord($&).
+# "[vs.".ord('a').",".ord('A').",".ord('0')."]".
+# " outuid=@outuid\n";
+
+ $mul *= $base;
+ s/^.//;
+ }
+ my $recons_long= join '_', @outuid;
+ $f->{UidLong} eq $recons_long or
+ die "$f->{UidLong} = $f->{UidShort} = $recons_long ?";
+ }
+
+ if (defined $qa->{"R$f->{UidShort}"} &&
+ !defined $qa->{"T$f->{UidShort}"}) {
+ $f->{Suppress}= 1;
+ }
+
+ push @flows, $f;
+
+</%perl>
+<& dumptable:row, qa => $qa, sth => $sth, row => $f &>
% }
<& dumptable:end, qa => $qa &>
% }
totalprofit:
".(join " +
- ", map { "$_->{unit_profit} $_->{Var}" } @flows)."
+ ", map {
+ sprintf "%.20f %s", $_->{ExpectedUnitProfit}, $_->{Var}
+ } @flows)."
Subject To
";
my %avail_csts;
foreach my $flow (@flows) {
+ if ($flow->{Suppress}) {
+ $cplex .= "
+ $flow->{Var} = 0
+";
+ next;
+ }
foreach my $od (qw(org dst)) {
- my $cstname= join '_',
+ my $cstname= join '_', (
'avail',
$flow->{'commodid'},
$od,
$flow->{"${od}_id"},
- $flow->{"${od}_price"};
+ $flow->{"${od}_price"},
+ $flow->{"${od}_stallid"},
+ );
+
push @{ $avail_csts{$cstname}{Flows} }, $flow->{Var};
$avail_csts{$cstname}{Qty}= $flow->{"${od}_qty"};
}
die unless $found_section;
};
-print join ' ', map { $_->{Optimal} } @flows;
-
-push @columns, qw(OptQty OptCapital OptProfit);
+$addcols->({}, qw(
+ OptQty
+ ));
+$addcols->({ Total => 0 }, qw(
+ OptCapital OptProfit
+ ));
</%perl>
% {
% my $cdspan= $qa->{ShowStalls} ? ' colspan=2' : '';
% my $cdstall= $qa->{ShowStalls} ? '<th>Stall</th>' : '';
-<table>
+<table rules=groups>
+<colgroup span=1>
+<colgroup span=2>
+<% $qa->{ShowStalls} ? '<colgroup span=2>' : '' %>
+<colgroup span=1>
+<colgroup span=2>
+<colgroup span=2>
+<colgroup span=2>
+<colgroup span=3>
+% if ($optimise) {
+<colgroup span=3>
+% }
<tr>
+<th>
<th<% $cdspan %>>Collect
<th<% $cdspan %>>Deliver
<th>
<th colspan=2>Profit
<th colspan=3>Max
% if ($optimise) {
-<th colspan=3>Suggested
+<th colspan=3>Planned
% }
<tr>
+<th>
<th>Island <% $cdstall %>
<th>Island <% $cdstall %>
<th>Commodity
<th>Qty
<th>Price
<th>Qty
-<th>Unit
<th>Margin
+<th>Unit
<th>Qty
<th>Capital
<th>Profit
% foreach my $flow (@flows) {
<tr>
-% foreach my $col (@columns) {
-% $flow->{MaxQty}= $flow->{'org_qty'} < $flow->{'dst_qty'}
-% ? $flow->{'org_qty'} : $flow->{'dst_qty'};
-% $flow->{MaxProfit}= $flow->{MaxQty} * $flow->{'unitprofit'};
-% $flow->{PctProfit}= sprintf "%3.1f%%",
-% $flow->{'dst_price'} * 100.0 / $flow->{'org_price'}
-% - 100.0;
-% $flow->{MaxCapital}= $flow->{MaxQty} * $flow->{'org_price'};
-<td><% $flow->{$col} |h %>
+<td><input type=hidden name=R<% $flow->{UidShort} %> value="">
+ <input type=checkbox name=T<% $flow->{UidShort} %> value=""
+ <% $flow->{Suppress} ? '' : 'checked' %> >
+% foreach my $ci (0..$#cols) {
+% my $col= $cols[$ci];
+% my $v= $flow->{$col->{Name}};
+% $col->{Total} += $v if defined $col->{Total};
+% $v='' if !$col->{Text} && !$v;
+<td <% $col->{Text} ? '' : 'align=right' %>><% $v |h %>
+% }
+% }
+<tr>
+<th>
+<th colspan=2>Total
+% foreach my $ci (2..$#cols) {
+% my $col= $cols[$ci];
+<td align=right>
+% if (defined $col->{Total}) {
+<% $col->{Total} |h %>
+% }
+% }
+</table>
+
+<input type=submit name=update value="Update">
+
+% if ($optimise) { # ========== TRADING PLAN ==========
+%
+% my $iquery= $dbh->prepare('SELECT islandname FROM islands
+% WHERE islandid = ?');
+%
+<h1>Voyage trading plan</h1>
+<table>
+% foreach my $i (0..$#islandids) {
+<tr><td colspan=4><strong>
+% $iquery->execute($islandids[$i]);
+% my ($islandname) = $iquery->fetchrow_array();
+% if (!$i) {
+Start at <% $islandname |h %>
+% } else {
+Sail to <% $islandname |h %>
+% }
+</strong>
+% foreach my $od (qw(dst org)) {
+% my $sign= $od eq 'dst' ? -1 : +1;
+% foreach my $f (sort {
+% $a->{'commodname'} cmp $b->{'commodname'}
+% or $sign * ($a->{"${od}_price"} <=> $b->{"${od}_price"})
+% or $a->{"${od}_stallname"} cmp $b->{"${od}_stallname"}
+% } @flows) {
+% next if $f->{Suppress};
+% next unless $f->{"${od}_id"} == $islandids[$i];
+% next unless $f->{OptQty};
+<tr>Buy or sell flow
% }
+% }
% }
</table>
+%
+% } # ========== TRADING PLAN ==========
<%init>
use CommodsWeb;