Archived
0

Increase search speed, add minimum vendor rating parameter for skip low profitable vendors

This commit is contained in:
iMoHax
2016-10-14 15:57:14 +03:00
parent 77c12f6194
commit 374e1d6c62
9 changed files with 197 additions and 105 deletions

View File

@@ -1,5 +1,6 @@
package ru.trader.analysis;
import org.jetbrains.annotations.Nullable;
import ru.trader.analysis.graph.Edge;
import ru.trader.core.Offer;
import ru.trader.core.Vendor;
@@ -165,6 +166,7 @@ public class CrawlerSpecificator {
if (res) return true;
for (TimeEntry<Offer> entry : offers){
Offer offer = entry.obj;
if (offer.getVendor().equals(vendor)) return true;
Offer sell = vendor.getSell(offer.getItem());
res = sell != null && sell.getCount() >= offer.getCount();
if (res) return true;
@@ -172,12 +174,23 @@ public class CrawlerSpecificator {
return false;
}
public Collection<Vendor> getVendors(Collection<Vendor> vendors){
Set<Vendor> v = containsAny.stream().map(e -> e.obj).collect(Collectors.toSet());
any.stream().map(e -> e.obj).forEach(v::add);
all.stream().map(e -> e.obj).forEach(v::add);
offers.stream().map(e -> e.obj.getVendor()).forEach(v::add);
v.addAll(vendors);
public Set<Vendor> getVendors(Collection<Scorer.Rating> vendors){
Set<Vendor> v = all.stream().map(e -> e.obj).collect(Collectors.toSet());
if (containsAny.size() > 0){
vendors.stream().filter(r -> contains(containsAny, r.getVendor())).sorted().limit(5).forEach(r -> v.add(r.getVendor()));
}
if (any.size() > 0){
vendors.stream().filter(r -> contains(any, r.getVendor())).sorted().limit(5).forEach(r -> v.add(r.getVendor()));
}
for (TimeEntry<Offer> entry : offers) {
vendors.stream().filter(r -> {
Offer offer = entry.obj;
if (offer.getVendor().equals(r.getVendor())) return true;
Offer sell = r.getVendor().getSell(offer.getItem());
return sell != null && sell.getCount() >= offer.getCount();
}).sorted().limit(5).forEach(r -> v.add(r.getVendor()));
}
return v;
}

View File

@@ -27,70 +27,26 @@ import java.util.stream.Stream;
public class Scorer {
private final static Logger LOG = LoggerFactory.getLogger(Scorer.class);
private final Map<Item, Offer> sellOffers;
private final Map<Item, Offer> buyOffers;
private final FilteredMarket market;
private final Profile profile;
private final double avgProfit;
private final double minProfit;
private final double maxProfit;
private final double maxScore;
private final double avgDistance;
public Scorer(FilteredMarket market, Profile profile) {
this.market = market;
this.profile = profile;
sellOffers = new HashMap<>(100, 0.9f);
buyOffers = new HashMap<>(100, 0.9f);
market.getItems().forEach(this::fillOffers);
DoubleSummaryStatistics statProfit = computeProfit();
minProfit = statProfit.getMin() / profile.getShip().getCargo();
avgProfit = statProfit.getAverage() / profile.getShip().getCargo();
maxProfit = statProfit.getMax() / profile.getShip().getCargo();
avgDistance = computeAvgDistance();
maxScore = getScore(1, statProfit.getMax(), 0, 1, 0, 0);
}
public Profile getProfile() {
return profile;
}
private void fillOffers(Item item){
Optional<Offer> offer = market.getSell(item).findFirst();
if (offer.isPresent()){
sellOffers.put(item, offer.get());
}
offer = market.getBuy(item).findFirst();
if (offer.isPresent()){
buyOffers.put(item, offer.get());
}
}
private DoubleSummaryStatistics computeProfit(){
return sellOffers.values().stream()
.flatMap(this::mapToOrder)
.collect(Collectors.summarizingDouble(Order::getProfit));
}
private double computeAvgDistance(){
OptionalDouble res = market.getVendors().mapToDouble(Vendor::getDistance).average();
return res.orElse(0);
}
public double getAvgProfit() {
return avgProfit;
}
public double getMaxProfit() {
return maxProfit;
}
public double getMaxScore() {
return maxScore;
}
public double getAvgDistance() {
return avgDistance;
}
@@ -157,25 +113,113 @@ public class Scorer {
return score;
}
private Stream<Order> mapToOrder(Offer offer) {
Offer sell;
Offer buy;
if (offer.getType() == OFFER_TYPE.SELL){
sell = offer;
buy = buyOffers.get(offer.getItem());
} else {
sell = sellOffers.get(offer.getItem());
buy = offer;
}
if (sell == null || buy == null) return Stream.empty();
Order order = new Order(sell, buy, profile.getBalance(), profile.getShip().getCargo());
if (order.getProfit() <= 0) return Stream.empty();
return Stream.of(order);
}
public List<Order> getOrders(Vendor seller, Vendor buyer){
FilteredVendor fSeller = market.getFiltered(seller);
FilteredVendor fBuyer = market.getFiltered(buyer);
return MarketUtils.getOrders(fSeller, fBuyer);
}
public RatingComputer getRatingComputer(final Set<Vendor> vendors){
return new RatingComputer(vendors);
}
public class RatingComputer {
private final Map<Item, Offer> sellOffers;
private final Map<Item, Offer> buyOffers;
private final DoubleSummaryStatistics globalStat;
private final double avgDistance;
private RatingComputer(final Set<Vendor> vendors) {
sellOffers = new HashMap<>(100, 0.9f);
buyOffers = new HashMap<>(100, 0.9f);
market.getItems().forEach(i -> fillOffers(i, vendors));
globalStat = computeProfit();
avgDistance = vendors.stream().mapToDouble(Vendor::getDistance).average().orElse(0);
}
private void fillOffers(Item item, Set<Vendor> vendors){
Optional<Offer> offer = market.getSell(item).filter(o -> vendors.contains(o.getVendor())).findFirst();
if (offer.isPresent()){
sellOffers.put(item, offer.get());
}
offer = market.getBuy(item).filter(o -> vendors.contains(o.getVendor())).findFirst();
if (offer.isPresent()){
buyOffers.put(item, offer.get());
}
}
private Stream<Order> mapToOrder(Offer offer) {
Offer sell;
Offer buy;
if (offer.getType() == OFFER_TYPE.SELL){
sell = offer;
buy = buyOffers.get(offer.getItem());
} else {
sell = sellOffers.get(offer.getItem());
buy = offer;
}
if (sell == null || buy == null) return Stream.empty();
Order order = new Order(sell, buy, profile.getBalance(), profile.getShip().getCargo());
if (order.getProfit() <= 0) return Stream.empty();
return Stream.of(order);
}
private DoubleSummaryStatistics computeProfit(){
return sellOffers.values().stream()
.flatMap(this::mapToOrder)
.collect(Collectors.summarizingDouble(Order::getProfit));
}
private DoubleSummaryStatistics computeProfits(Stream<Order> orders) {
return orders.sorted(Comparator.<Order>reverseOrder())
.limit(4)
.filter(o -> o.getProfit() > 0)
.collect(Collectors.summarizingDouble(Order::getProfit));
}
public Rating getRating(Vendor vendor){
Stream<Order> sell = vendor.getAllSellOffers().stream().flatMap(this::mapToOrder);
Stream<Order> buy = vendor.getAllBuyOffers().stream().flatMap(this::mapToOrder);
DoubleSummaryStatistics sellStat = computeProfits(sell);
DoubleSummaryStatistics buyStat = computeProfits(buy);
double sellRate = 0.5 * sellStat.getMax() / globalStat.getMax() + 2.5 * sellStat.getAverage() / globalStat.getAverage();
double buyRate = 0.5 * buyStat.getMax() / globalStat.getMax() + 2 * buyStat.getAverage() / globalStat.getAverage();
double distRate = 0.5 * (vendor.getDistance() > 0 ? (vendor.getDistance() < avgDistance ? 1-vendor.getDistance()/avgDistance : -1+avgDistance/vendor.getDistance()) : 0.0);
LOG.trace("Computed rate for {} = {}", vendor.getFullName(), sellRate + buyRate + distRate);
LOG.trace("global - max: {} avg: {} min: {}", globalStat.getMax(), globalStat.getAverage(), globalStat.getMin());
LOG.trace("sell - max: {} avg: {} min: {} rate: {}", sellStat.getMax(), sellStat.getAverage(), sellStat.getMin(), sellRate);
LOG.trace("buy - max: {} avg: {} min: {} rate: {}", buyStat.getMax(), buyStat.getAverage(), buyStat.getMin(), buyRate);
LOG.trace("distance: {} avg: {} rate: {}", vendor.getDistance(), avgDistance, distRate);
return new Rating(vendor, sellRate + buyRate + distRate);
}
}
public class Rating implements Comparable<Rating> {
private final Vendor vendor;
private final double rate;
public Rating(Vendor vendor, double rate) {
this.vendor = vendor;
this.rate = rate;
}
public Vendor getVendor() {
return vendor;
}
public double getRate() {
return rate;
}
@Override
public int compareTo(Rating o) {
return Double.compare(rate, o.rate);
}
}
}

View File

@@ -296,13 +296,26 @@ public class MarketAnalyzer {
private Collection<Vendor> getVendors(CrawlerSpecificator specificator, boolean withTransit){
List<Vendor> transits = withTransit ? market.get().map(Place::asTransit).collect(Collectors.toList()) : new ArrayList<>();
Collection<Vendor> vendors;
Set<Vendor> vendors;
if (!specificator.isFullScan() || specificator.getMinHop() >= profile.getLands()){
vendors = market.getMarkets().filter(specificator::contains).collect(Collectors.toList());
vendors = market.getMarkets().filter(specificator::contains).collect(Collectors.toSet());
} else {
vendors = market.getMarkets().collect(Collectors.toList());
vendors = market.getMarkets().collect(Collectors.toSet());
}
vendors = specificator.getVendors(vendors);
Scorer.RatingComputer ratings = searcher.getScorer().getRatingComputer(vendors);
Collection<Scorer.Rating> removes = new ArrayList<>(500);
vendors.removeIf(v -> {
Scorer.Rating rating = ratings.getRating(v);
if (rating.getRate() <= profile.getMinVendorRating()){
if (specificator.contains(v)){
removes.add(rating);
}
return true;
}
return false;
});
vendors.addAll(specificator.getVendors(removes));
vendors.addAll(transits);
return vendors;
}

View File

@@ -15,6 +15,7 @@ public class Profile {
private int lands;
private boolean refill;
private int routesCount;
private double minVendorRating;
//Scorer multipliers
private double distanceTime;
private double jumpTime;
@@ -31,6 +32,7 @@ public class Profile {
jumps = 3;
lands = 4;
routesCount = 30;
minVendorRating = 7;
distanceTime = 0.3;
fuelPrice = 100;
landingTime = 80;
@@ -49,6 +51,7 @@ public class Profile {
this.lands = profile.lands;
this.refill = profile.refill;
this.routesCount = profile.routesCount;
this.minVendorRating = profile.minVendorRating;
this.distanceTime = profile.distanceTime;
this.jumpTime = profile.jumpTime;
this.landingTime = profile.landingTime;
@@ -142,6 +145,14 @@ public class Profile {
this.routesCount = routesCount;
}
public double getMinVendorRating() {
return minVendorRating;
}
public void setMinVendorRating(double minVendorRating) {
this.minVendorRating = minVendorRating;
}
public double getDistanceTime() {
return distanceTime;
}
@@ -230,6 +241,7 @@ public class Profile {
profile.setLands(Integer.valueOf(values.getProperty("profile.lands", "4")));
profile.setPathPriority(PATH_PRIORITY.valueOf(values.getProperty("profile.search.priority", "FAST")));
profile.setRoutesCount(Integer.valueOf(values.getProperty("profile.search.routes", "30")));
profile.setMinVendorRating(Double.valueOf(values.getProperty("profile.search.minRating", "7")));
profile.setFuelPrice(Double.valueOf(values.getProperty("profile.search.fuel.price", "100")));
profile.setDistanceTime(Double.valueOf(values.getProperty("profile.search.times.distance", "0.3")));
profile.setLandingTime(Double.valueOf(values.getProperty("profile.search.times.landing", "80")));
@@ -250,6 +262,7 @@ public class Profile {
values.setProperty("profile.lands", String.valueOf(lands));
values.setProperty("profile.search.priority", String.valueOf(pathPriority));
values.setProperty("profile.search.routes", String.valueOf(routesCount));
values.setProperty("profile.search.minRating", String.valueOf(minVendorRating));
values.setProperty("profile.search.fuel.price", String.valueOf(fuelPrice));
values.setProperty("profile.search.times.distance", String.valueOf(distanceTime));
values.setProperty("profile.search.times.landing", String.valueOf(landingTime));

View File

@@ -36,7 +36,7 @@ public class ScorerTest extends Assert {
profile.setBalance(1000000);
Scorer scorer = new Scorer(fWorld, profile);
double avgProfit = scorer.getAvgProfit() * profile.getShip().getCargo();
double avgProfit = 750 * profile.getShip().getCargo();
double score = scorer.getScore(scorer.getAvgDistance(), 0, 1, 1, 0, 4);
double score1 = scorer.getScore(scorer.getAvgDistance(), avgProfit/2, 1, 1, 0, 4);