Increase search speed, add minimum vendor rating parameter for skip low profitable vendors
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user