emlab.role.market.AbstractMarketRole.java Source code

Java tutorial

Introduction

Here is the source code for emlab.role.market.AbstractMarketRole.java

Source

/*******************************************************************************
 * Copyright 2012 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package emlab.role.market;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import org.springframework.transaction.annotation.Transactional;

import agentspring.role.AbstractRole;
import emlab.domain.market.Bid;
import emlab.domain.market.ClearingPoint;
import emlab.domain.market.DecarbonizationMarket;
import emlab.domain.market.electricity.ElectricitySpotMarket;
import emlab.repository.Reps;

/**
 * Calculates {@link ClearingPoint} for any {@link Market}.
 * 
 * @author <a href="mailto:E.J.L.Chappin@tudelft.nl">Emile Chappin</a>
 */
public abstract class AbstractMarketRole<T extends DecarbonizationMarket> extends AbstractRole<T> {

    @Autowired
    Neo4jTemplate template;

    @Autowired
    Reps reps;

    @Transactional
    public ClearingPoint calculateClearingPoint(DecarbonizationMarket market, long time) {
        double clearedVolume = 0d;
        double clearedPrice = 0d;
        double totalSupplyPrice = calculateTotalSupplyPriceForMarketForTime(market, time);
        double totalSupply = calculateTotalSupplyForMarketForTime(market, time);
        logger.info("total supply {} total price {}", totalSupply, totalSupplyPrice);
        double totalDemandForPrice = calculateTotalDemandForMarketForTimeForPrice(market, time, totalSupplyPrice);
        logger.info("total demand {} for price {}", totalDemandForPrice, totalSupplyPrice);

        // Not enough to meet demand
        if (totalDemandForPrice > totalSupply) {
            clearedVolume = totalSupply;
            if (market.isAuction()) {
                clearedPrice = calculateTotalDemandForMarketForTimeForPrice(market, time, 0d);
            } else {
                clearedPrice = market instanceof ElectricitySpotMarket
                        ? ((ElectricitySpotMarket) market).getValueOfLostLoad()
                        : totalSupplyPrice;
            }
        } else { // Supply exceeds demand
            double totalOfferAmount = 0d;
            double previousPrice = 0d;
            for (Bid offer : reps.bidRepository.findOffersForMarketForTime(market, time)) {
                double price = offer.getPrice();
                double amount = offer.getAmount();
                double demand = calculateTotalDemandForMarketForTimeForPrice(market, time, price);
                if (demand < totalOfferAmount + amount) {
                    if (demand == 0) {
                        if (getCurrentTick() > 0) {
                            ClearingPoint cp = reps.clearingPointRepository
                                    .findClearingPointForMarketAndTime(market, getCurrentTick() - 1);
                            if (cp != null)
                                previousPrice = cp.getPrice();
                        }
                        clearedPrice = previousPrice;
                        clearedVolume = totalOfferAmount;
                    } else if (totalOfferAmount >= demand) {
                        clearedPrice = previousPrice;
                        clearedVolume = totalOfferAmount;
                    } else {
                        clearedPrice = price;
                        clearedVolume = demand;
                    }
                    break;
                }
                totalOfferAmount += amount;
                previousPrice = price;
            }
        }
        ClearingPoint point = new ClearingPoint().persist();
        point.setAbstractMarket(market);
        point.setTime(time);
        point.setPrice(Math.max(0, clearedPrice));
        point.setVolume(clearedVolume);

        // set bids to accepted and check for partial acceptance
        // DEMAND
        double previousPrice = markAcceptedBids(point, false);
        // if auction - last accepted demand bid sets the price
        if (market.isAuction()) {
            point.setPrice(Math.max(0, previousPrice));
        }
        // SUPPLY
        markAcceptedBids(point, true);
        return point;
    }

    private double markAcceptedBids(ClearingPoint point, boolean isSupply) {
        long time = point.getTime();
        DecarbonizationMarket market = point.getAbstractMarket();
        double clearedPrice = point.getPrice();
        double clearedVolume = point.getVolume();
        double totalBidVolume = 0d;
        double previousPrice = Double.NEGATIVE_INFINITY;
        double accpetedSamePriceVolume = 0d;

        Iterable<Bid> bids = isSupply
                ? reps.bidRepository.findOffersForMarketForTimeBelowPrice(market, time, clearedPrice)
                : market.isAuction() ? reps.bidRepository.findDemandBidsForMarketForTime(market, time)
                        : reps.bidRepository.findDemandBidsForMarketForTimeAbovePrice(market, time, clearedPrice);

        for (Bid bid : bids) {
            double amount = bid.getAmount();
            totalBidVolume += amount;
            accpetedSamePriceVolume = bid.getPrice() == previousPrice ? accpetedSamePriceVolume + amount : amount;
            if (totalBidVolume < clearedVolume) {
                bid.setStatus(Bid.ACCEPTED);
                bid.setAcceptedAmount(bid.getAmount());
            } else {
                double lastAvailableBidSize = clearedVolume - (totalBidVolume - accpetedSamePriceVolume);
                double samePriceVolume = calculateBidsForMarketForTimeForPrice(market, time, bid.getPrice(),
                        isSupply);
                double adjustRatio = lastAvailableBidSize / samePriceVolume;
                for (Bid partBid : isSupply
                        ? reps.bidRepository.findOffersForMarketForTimeForPrice(market, time, bid.getPrice())
                        : reps.bidRepository.findDemandBidsForMarketForTimeForPrice(market, time, bid.getPrice())) {
                    partBid.setStatus(Bid.PARTLY_ACCEPTED);
                    partBid.setAcceptedAmount(partBid.getAmount() * adjustRatio);
                }
                break;
            }
            previousPrice = bid.getPrice();
        }
        return previousPrice;
    }

    private double calculateBidsForMarketForTimeForPrice(DecarbonizationMarket market, long time, double price,
            boolean isSupply) {
        try {
            return isSupply ? reps.bidRepository.calculateOffersForMarketForTimeForPrice(market, time, price)
                    : reps.bidRepository.calculateDemandBidsForMarketForTimeForPrice(market, time, price);
        } catch (NullPointerException npe) {
        }
        return 0d;
    }

    private double calculateTotalDemandForMarketForTimeForPrice(DecarbonizationMarket market, long time,
            double price) {
        try {
            return market.isAuction() ? reps.bidRepository.calculateTotalDemandForMarketForTime(market, time)
                    : reps.bidRepository.calculateTotalDemandForMarketForTimeForPrice(market, time, price);
        } catch (NullPointerException npe) {
        }
        return 0d;
    }

    private double calculateTotalSupplyPriceForMarketForTime(DecarbonizationMarket market, long time) {
        try {
            return reps.bidRepository.calculateTotalSupplyPriceForMarketForTime(market, time);
        } catch (NullPointerException e) {
        }
        return 0d;
    }

    private double calculateTotalSupplyForMarketForTime(DecarbonizationMarket market, long time) {
        try {
            return reps.bidRepository.calculateTotalSupplyForMarketForTime(market, time);
        } catch (NullPointerException e) {
        }
        return 0d;
    }

    public abstract Reps getReps();

    // -------------------------------------------------------------------------------------------------

    // /**
    // * Old clearing algorithm. Not deleted yet for compatability reasons. WARNS if it is used.
    // *
    // * @param market
    // * @param supplyBidsIterable
    // * @param demandBidsIterable
    // * @param time
    // * @return
    // */
    // public ClearingPoint calculateClearingPoint(DecarbonizationMarket market, Iterable<Bid> supplyBidsIterable,
    // Iterable<Bid> demandBidsIterable, long time) {
    //
    // logger.warn("{} still used the old clearing algorithm!", market);
    //
    // List<Bid> supplyBids = Utils.asList(supplyBidsIterable);
    // List<Bid> demandBids = Utils.asList(demandBidsIterable);
    //
    // logger.info("Number of supply bids: " + supplyBids.size() + " and demand: " + demandBids.size());
    //
    // if (supplyBids.size() == 0 || demandBids.size() == 0) {
    // logger.info("Either no supply bids or no demand bids - supply: {}; demand: {}", +supplyBids.size(), demandBids.size());
    // return null;
    // } else {
    // logger.info("{} supply bids and {} demand bids present on " + market, supplyBids.size(), demandBids.size());
    // }
    //
    // double totalSupply = 0d;
    // for (Bid bid : supplyBids) {
    // totalSupply += bid.getAmount();
    // }
    // double totalDemand = 0d;
    // for (Bid bid : demandBids) {
    // totalDemand += bid.getAmount();
    // }
    // logger.info("Total supply: {} -- total demand: {}", totalSupply, totalDemand);
    //
    // Collections.sort(supplyBids, new BidPriceComparator());
    // Collections.sort(demandBids, new BidPriceReverseComparator());
    // logger.info("Bids sorted on price");
    //
    // // TODO check whether there are negative amounts bid. Negative prices
    // // may be ok, possibly warn. Negative amounts are not allowed.
    //
    // boolean settled = false;
    // double price = 0d;
    // double amount = 0d;
    //
    // double totalSupplyAccepted = 0d;
    // double totalDemandAccepted = 0d;
    // double lastSupplyPrice = 0d;
    // double lastDemandPrice = 0d;
    //
    // int bidDemandIndex = 0;
    // int bidSupplyIndex = 0;
    //
    // boolean done = false;
    //
    // // SUPPLY LOOP
    // while (!done) {
    // boolean supplyBidMet = false;
    //
    // if (supplyBids.size() <= bidSupplyIndex) {
    // logger.info("No more supply bids");
    // // no more supply bids.
    // done = true;
    // settled = true;
    //
    // amount = totalSupplyAccepted;
    // if (market.isAuction()) {
    // price = lastDemandPrice;
    // } else {
    // price = lastSupplyPrice;
    // }
    //
    // logger.info("Accepted the last demand bid partly as a final bid");
    //
    // // Supply bid is accepted
    // double partialAcceptance = totalSupplyAccepted - totalDemandAccepted;
    //
    // calculateAndDetermineSharedAcceptance(demandBids, demandBids.get(bidDemandIndex), partialAcceptance);
    //
    // totalDemandAccepted += partialAcceptance;
    //
    // logger.info("Cleared: partial demand bid, no more supply bids.");
    //
    // } else {
    // double supplyAmount = supplyBids.get(bidSupplyIndex).getAmount();
    // double supplyPrice = supplyBids.get(bidSupplyIndex).getPrice();
    //
    // // DEMAND LOOP
    // while (!supplyBidMet && !done) {
    //
    // if (demandBids.size() <= bidDemandIndex) {
    // // no more demand bids;
    // logger.info("No more demand bids, settle with accepted demand so far. Maybe a partial supply bid.");
    // done = true;
    //
    // if ((totalDemandAccepted) > (totalSupplyAccepted)) {
    //
    // settled = true;
    // logger.info("Accepted a partial supply bid as a final bid");
    // logger.info("Accepted a demand bid as a final bid");
    //
    // demandBids.get(bidDemandIndex - 1).updateStatus(Bid.ACCEPTED);
    //
    // double partialAcceptance = totalDemandAccepted - totalSupplyAccepted;
    //
    // calculateAndDetermineSharedAcceptance(supplyBids, supplyBids.get(bidSupplyIndex), partialAcceptance);
    //
    // logger.info("Supply bid part accepted is " + partialAcceptance);
    // totalSupplyAccepted += partialAcceptance;
    // price = supplyPrice;
    // amount = totalSupplyAccepted;
    // logger.info("Done case 2: partial supply bid.");
    // }
    //
    // } else {
    // double demandAmount = demandBids.get(bidDemandIndex).getAmount();
    // double demandPrice = demandBids.get(bidDemandIndex).getPrice();
    // lastDemandPrice = demandPrice;
    //
    // // logger.info("Demand price: " + demandPrice);
    //
    // // Should this demand bid be accepted? If true, the
    // // demand bid is still above the supply bid on the bid
    // // ladder.
    //
    // if (supplyPrice <= demandPrice) {
    //
    // // Is this demand bid is smaller in amount than the
    // // supply bid including the current one?
    // if ((totalDemandAccepted + demandAmount) < (totalSupplyAccepted + supplyAmount)) {
    //
    // logger.info("Accepted a demand bid");
    // // Demand bid is accepted
    // demandBids.get(bidDemandIndex).updateStatus(Bid.ACCEPTED);
    // // lastDemandPrice = demandPrice;
    // totalDemandAccepted += demandAmount;
    // bidDemandIndex++;
    //
    // } else if ((totalDemandAccepted + demandAmount) == (totalSupplyAccepted + supplyAmount)) {
    //
    // logger.info("Accepted both a demand and a supply bid");
    // // Demand and supply bids are accepted
    // logger.info("Bid repository {}", getReps().bidRepository);
    // demandBids.get(bidDemandIndex).updateStatus(Bid.ACCEPTED);
    // supplyBids.get(bidSupplyIndex).updateStatus(Bid.ACCEPTED);
    // // lastDemandPrice = demandPrice;
    // lastSupplyPrice = supplyPrice;
    // totalDemandAccepted += demandAmount;
    // totalSupplyAccepted += supplyAmount;
    // bidDemandIndex++;
    // bidSupplyIndex++;
    //
    // } else {
    //
    // // this demand bid is larger then the supply
    // // bid, we should check the next supply bid and
    // // leave the demand bid as it is
    // logger.info("Accepted a supply bid");
    // // Supply bid is accepted
    // supplyBids.get(bidSupplyIndex).updateStatus(Bid.ACCEPTED);
    // supplyBidMet = true; // We go out the demand
    // // loop
    // bidSupplyIndex++;
    // lastSupplyPrice = supplyPrice;
    // totalSupplyAccepted += supplyAmount;
    // }
    // } else {
    // logger.info("Demand curve now below supply curve, so the clearing has been passed");
    // done = true;
    //
    // if (totalDemandAccepted == totalSupplyAccepted) {
    // // We have not settled yet!
    // logger.info("We are in balance. Settle later.");
    // } else {
    // if ((totalDemandAccepted + demandAmount) > (totalSupplyAccepted)) {
    //
    // settled = true;
    // // this demand bid is larger then the supply
    // // bid, we should check the next supply bid
    // // and leave the demand bid as it is
    // logger.info("Accepted a partial demand bid as a final bid");
    // // Supply bid is accepted
    //
    // supplyBids.get(bidSupplyIndex).updateStatus(Bid.FAILED);
    // // lastDemandPrice = demandPrice;
    // double partialAcceptance = totalSupplyAccepted - totalDemandAccepted;
    //
    // calculateAndDetermineSharedAcceptance(demandBids, demandBids.get(bidDemandIndex), partialAcceptance);
    //
    // logger.info("Demand bid part accepted is " + partialAcceptance);
    // totalDemandAccepted += partialAcceptance;
    // price = demandPrice;
    // amount = totalDemandAccepted;
    // logger.info("Done case 3: partial demand bid.");
    // }
    // }
    //
    // }
    //
    // }
    // if (done && !settled) {
    //
    // // supply is now larger then demand. We're done
    // // somewhere in
    // // this area. With, without this bid, or something.
    //
    // // Found a price and a demand
    // // logger.info("Done but not yet settled");
    // // logger.info("Total supply: " + totalSupplyAccepted);
    // // logger.info("Total demand: " + totalDemandAccepted);
    //
    // // Case 1. We're exactly on the crossing
    // if (totalDemandAccepted == totalSupplyAccepted) {
    // logger.info("Done case 4: even bid.");
    // amount = totalSupplyAccepted;
    // price = lastSupplyPrice;
    // supplyBidMet = true;
    // }
    // }
    // }
    //
    // }
    // }
    // logger.info("Cleared with amount: " + amount + " and price: " + price);
    //
    // ClearingPoint clearingPoint = getReps().clearingPointRepositoryOld.createOrUpdateClearingPoint(market, price, amount, time);
    //
    // // Setting all bids that are still Submitted to Failed
    // for (Bid bid : demandBids) {
    // if (bid.getStatus() == Bid.SUBMITTED) {
    // bid.updateStatus(Bid.FAILED);
    // }
    // }
    // for (Bid bid : supplyBids) {
    // if (bid.getStatus() == Bid.SUBMITTED) {
    // bid.updateStatus(Bid.FAILED);
    // }
    // }
    // logger.info("Set other bids to failed");
    // return clearingPoint;
    // }
    //
    // private void calculateAndDetermineSharedAcceptance(List<Bid> bids, Bid bid, double partialAcceptance) {
    // // check whether there are more demand bids that match the same
    // // price level and make them all accept part
    // ArrayList<Bid> bidsThatAreAllToBePartial = getBidsWithSamePrice(bids, bid);
    // double ratio = partialAcceptance / getTotalVolumeOfBids(bidsThatAreAllToBePartial);
    // for (Bid _bid : bidsThatAreAllToBePartial) {
    // _bid.updateStatus(Bid.PARTLY_ACCEPTED);
    // overrideBidAmount(_bid, ratio);
    // }
    //
    // logger.info("For " + bidsThatAreAllToBePartial.size() + " demand bid(s) they are partly accepted, total " + partialAcceptance
    // + ", ratio: " + ratio);
    //
    // }
    //
    // private void overrideBidAmount(Bid bid, double ratio) {
    // bid.updateAmount(bid.getAmount() * ratio);
    // }
    //
    // private ArrayList<Bid> getBidsWithSamePrice(List<Bid> bids, Bid bidToMatch) {
    // logger.info("Finding bids in a list with the same price as some bid");
    // ArrayList<Bid> bidsThatMatch = new ArrayList<Bid>();
    //
    // for (Bid bid : bids) {
    // if (bid.getPrice() == bidToMatch.getPrice()) {
    // bidsThatMatch.add(bid);
    // }
    // }
    // return bidsThatMatch;
    // }
    //
    // private double getTotalVolumeOfBids(List<Bid> bids) {
    // logger.info("Calculating volumes of a number of bids");
    // double volume = 0d;
    // for (Bid bid : bids) {
    // volume += bid.getAmount();
    // }
    // return volume;
    // }
    //
    // //
    // // public SegmentClearingPoint
    // // calculateSegmentClearingPoint(DecarbonizationMarket market,
    // // Iterable<ElectricitySpotBid> supplyBidsIterable,
    // // Iterable<ElectricitySpotBid> demandBidsIterable, long time, Segment
    // // segment) {
    // //
    // // List<Bid> demandBids = Utils.asDownCastedList(demandBidsIterable);
    // // List<Bid> supplyBids = Utils.asDownCastedList(supplyBidsIterable);
    // //
    // // ClearingPoint clearingPoint = calculateClearingPoint(market, supplyBids,
    // // demandBids, time);
    // //
    // // if (clearingPoint != null) {
    // // SegmentClearingPoint segmentClearingPoint =
    // // getClearingPointRepository().createOrUpdateSegmentClearingPoint(segment,
    // // clearingPoint.getAbstractMarket(), clearingPoint.getPrice(),
    // // clearingPoint.getVolume(), clearingPoint.getTime(),
    // // clearingPoint.getIteration());
    // //
    // // return segmentClearingPoint;
    // // } else {
    // // return null;
    // // }
    // //
    // // }

}