Java tutorial
/* * TacTex - a power trading agent that competed in the Power Trading Agent Competition (Power TAC) www.powertac.org * Copyright (c) 2013-2016 Daniel Urieli and Peter Stone {urieli,pstone}@cs.utexas.edu * * * This file is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * */ package edu.utexas.cs.tactex; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.TreeMap; import org.apache.commons.math3.linear.ArrayRealVector; import org.joda.time.Instant; import org.junit.Before; import org.junit.Test; import org.powertac.common.Competition; import org.powertac.common.BalancingTransaction; import org.powertac.common.Broker; import org.powertac.common.ClearedTrade; import org.powertac.common.CustomerInfo; import org.powertac.common.MarketTransaction; import org.powertac.common.Order; import org.powertac.common.Orderbook; import org.powertac.common.OrderbookOrder; import org.powertac.common.TariffSpecification; import org.powertac.common.TariffTransaction; import org.powertac.common.TimeService; import org.powertac.common.Timeslot; import org.powertac.common.enumerations.PowerType; import org.powertac.common.msg.MarketBootstrapData; import org.powertac.common.repo.TimeslotRepo; import org.springframework.test.util.ReflectionTestUtils; import edu.utexas.cs.tactex.ConfiguratorFactoryService; import edu.utexas.cs.tactex.MarketManagerService; import edu.utexas.cs.tactex.PortfolioManagerService; import edu.utexas.cs.tactex.MarketManagerService.DPResult; import edu.utexas.cs.tactex.core.PowerTacBroker; import edu.utexas.cs.tactex.utils.BrokerUtils.PriceMwhPair; /** * @author urieli * */ public class MarketManagerTest { public class MarketMgrThatDoesntSend extends MarketManagerService { @Override protected void submitOrder(double neededKWh, int timeslot, int currentTimeslotIndex, List<Timeslot> enabledTimeslots) { } } private MarketMgrThatDoesntSend marketManagerService; private ConfiguratorFactoryService configuratorFactoryService; private PowerTacBroker brokerContext; private Broker thebroker; private Competition competition; private PortfolioManagerService portfolioManagerService; private Timeslot currentTimeslot; private List<Timeslot> enabledTimeslots; private TimeslotRepo timeslotRepo; static final private int IGNORED_TIME_SLOTS = 1; private double[] mwh; private double[] price; /** * @author urieli * */ @Before public void setUp() throws Exception { marketManagerService = new MarketMgrThatDoesntSend(); competition = mock(Competition.class); when(competition.getBootstrapDiscardedTimeslots()).thenReturn(IGNORED_TIME_SLOTS); Competition.setCurrent(competition); configuratorFactoryService = new ConfiguratorFactoryService(); ConfiguratorFactoryService.GlobalConstants constants = configuratorFactoryService.new GlobalConstants(); ReflectionTestUtils.setField(constants, "USAGE_RECORD_LENGTH", 3); ReflectionTestUtils.setField(constants, "MARKET_TRADES_ALPHA", 0.3); ReflectionTestUtils.setField(configuratorFactoryService, "CONSTANTS", constants); ReflectionTestUtils.setField(marketManagerService, "configuratorFactoryService", configuratorFactoryService); brokerContext = mock(PowerTacBroker.class); thebroker = new Broker("testBroker"); when(brokerContext.getBroker()).thenReturn(thebroker); when(brokerContext.getBrokerUsername()).thenReturn(thebroker.getUsername()); portfolioManagerService = mock(PortfolioManagerService.class); ReflectionTestUtils.setField(marketManagerService, "portfolioManager", portfolioManagerService); currentTimeslot = new Timeslot(363, null); enabledTimeslots = new ArrayList<Timeslot>(); enabledTimeslots.add(new Timeslot(364, null)); // -1 enabledTimeslots.add(new Timeslot(365, null)); // -2 timeslotRepo = mock(TimeslotRepo.class); ReflectionTestUtils.setField(marketManagerService, "timeslotRepo", timeslotRepo); when(timeslotRepo.enabledTimeslots()).thenReturn(enabledTimeslots); } private void initWithBootData() { // initialize() is tested separately and should // work correctly for this test marketManagerService.initialize(brokerContext); mwh = new double[] { 1, 2, 3, 1, 2, 3 }; price = new double[] { -10, -20, -10, -20, -10, -20 }; MarketBootstrapData boot = new MarketBootstrapData(mwh, price); marketManagerService.handleMessage(boot); } /** * Test */ @Test public void test_initialize() { // initialize with arbitrary values ReflectionTestUtils.setField(marketManagerService, "marketTotalMwh", 1234.0); ReflectionTestUtils.setField(marketManagerService, "marketTotalPayments", 1234.0); ReflectionTestUtils.setField(marketManagerService, "lastOrder", null); ReflectionTestUtils.setField(marketManagerService, "marketMWh", null); ReflectionTestUtils.setField(marketManagerService, "marketPayments", null); ReflectionTestUtils.setField(marketManagerService, "predictedUsage", null); ReflectionTestUtils.setField(marketManagerService, "actualUsage", null); ReflectionTestUtils.setField(marketManagerService, "orderbooks", null); ReflectionTestUtils.setField(marketManagerService, "maxTradePrice", 1234); ReflectionTestUtils.setField(marketManagerService, "minTradePrice", 1234); ReflectionTestUtils.setField(marketManagerService, "supportingBidGroups", null); ReflectionTestUtils.setField(marketManagerService, "dpCache2013", null); ReflectionTestUtils.setField(marketManagerService, "shortBalanceTransactionsData", null); ReflectionTestUtils.setField(marketManagerService, "surplusBalanceTransactionsData", null); // initialize should set all fields correctly marketManagerService.initialize(brokerContext); // get fields from object and verify they are initialized correctly double marketTotalMwh = (Double) ReflectionTestUtils.getField(marketManagerService, "marketTotalMwh"); assertEquals("marketTotalMwh", 0.0, marketTotalMwh, 1e-6); double marketTotalPayments = (Double) ReflectionTestUtils.getField(marketManagerService, "marketTotalPayments"); assertEquals("marketTotalPayments", 0.0, marketTotalPayments, 1e-6); // map should be initialized to empty @SuppressWarnings("unchecked") HashMap<Integer, Order> lastOrder = (HashMap<Integer, Order>) ReflectionTestUtils .getField(marketManagerService, "lastOrder"); assertNotNull("lastOrder", lastOrder); assertEquals("lastOrder.length", 0, lastOrder.size()); // arrays are not null, of the right size, and initialized // with 0's int usageRecordLength = configuratorFactoryService.CONSTANTS.USAGE_RECORD_LENGTH(); double[] marketMWh = (double[]) ReflectionTestUtils.getField(marketManagerService, "marketMWh"); assertNotNull("marketMWh", marketMWh); assertEquals("length of marketMWh", usageRecordLength, marketMWh.length); assertArrayEquals(new double[usageRecordLength], marketMWh, 1e-6); double[] marketPayments = (double[]) ReflectionTestUtils.getField(marketManagerService, "marketPayments"); assertNotNull("marketPayments", marketPayments); assertEquals("length of marketPayments", usageRecordLength, marketPayments.length); assertArrayEquals(new double[usageRecordLength], marketPayments, 1e-6); double[][] predictedUsage = (double[][]) ReflectionTestUtils.getField(marketManagerService, "predictedUsage"); assertNotNull("predictedUsage", predictedUsage); assertEquals("predictedUsage.size", 24, predictedUsage.length); for (double[] p : predictedUsage) { assertNotNull("predictedUsage[i]", p); assertEquals("p.size", 2500, p.length); assertEquals("p[i]", (Integer) 0, p[0], 1e-6); } double[] actualUsage = (double[]) ReflectionTestUtils.getField(marketManagerService, "actualUsage"); assertNotNull("actualUsage", actualUsage); assertEquals("actualUsage.size", 2500, actualUsage.length); assertEquals("actualUsage[i]", 0, actualUsage[0], 1e-6); @SuppressWarnings("unchecked") HashMap<Integer, Orderbook> orderbooks = (HashMap<Integer, Orderbook>) ReflectionTestUtils .getField(marketManagerService, "orderbooks"); assertNotNull("orderbooks", orderbooks); assertEquals("orderbooks.length", 0, orderbooks.size()); double maxTradePrice = (Double) ReflectionTestUtils.getField(marketManagerService, "maxTradePrice"); assertEquals("maxTradePrice", -Double.MAX_VALUE, maxTradePrice, 1e-6); double minTradePrice = (Double) ReflectionTestUtils.getField(marketManagerService, "minTradePrice"); assertEquals("minTradePrice", Double.MAX_VALUE, minTradePrice, 1e-6); @SuppressWarnings("unchecked") TreeMap<Integer, TreeMap<Double, Double>> supportingBidGroups = (TreeMap<Integer, TreeMap<Double, Double>>) ReflectionTestUtils .getField(marketManagerService, "supportingBidGroups"); assertNotNull("supportingBidGroups", supportingBidGroups); assertEquals("supportingBidGroups.length", 0, supportingBidGroups.size()); MarketManagerService.DPCache dpCache2013 = (MarketManagerService.DPCache) ReflectionTestUtils .getField(marketManagerService, "dpCache2013"); assertNotNull("dpCache2013", dpCache2013); @SuppressWarnings("unchecked") ArrayList<Double> bestActions = (ArrayList<Double>) ReflectionTestUtils.getField(dpCache2013, "bestActions"); assertNotNull("dpCache2013.bestActions", bestActions); @SuppressWarnings("unchecked") ArrayList<Double> stateValues = (ArrayList<Double>) ReflectionTestUtils.getField(dpCache2013, "stateValues"); assertNotNull("dpCache2013.stateValues", stateValues); @SuppressWarnings("unchecked") HashMap<Integer, Boolean> validTimeslots = (HashMap<Integer, Boolean>) ReflectionTestUtils .getField(dpCache2013, "validTimeslots"); assertNotNull("dpCache2013.validTimeslots", validTimeslots); assertEquals("dpCache2013.bestActions", 0, dpCache2013.getBestActions().size()); assertEquals("dpCache2013.stateValues", 0, dpCache2013.getStateValues().size()); assertEquals("dpCache2013.valid(ts)", false, dpCache2013.isValid(currentTimeslot.getSerialNumber())); // map should be initialized to empty @SuppressWarnings("unchecked") ArrayList<PriceMwhPair> shortBalanceTransactionsData = (ArrayList<PriceMwhPair>) ReflectionTestUtils .getField(marketManagerService, "shortBalanceTransactionsData"); assertNotNull("shortBalanceTransactionsData", shortBalanceTransactionsData); assertEquals("shortBalanceTransactionsData.length", 0, shortBalanceTransactionsData.size()); @SuppressWarnings("unchecked") ArrayList<PriceMwhPair> surplusBalanceTransactionsData = (ArrayList<PriceMwhPair>) ReflectionTestUtils .getField(marketManagerService, "surplusBalanceTransactionsData"); assertNotNull("surplusBalanceTransactionsData", surplusBalanceTransactionsData); assertEquals("surplusBalanceTransactionsData.length", 0, surplusBalanceTransactionsData.size()); } /** * testing the usage predictions vs actual */ @Test public void testPredictedVsActualUsage() { // initialize should set all fields correctly // test_initialize() should verify it works fine marketManagerService.initialize(brokerContext); // test: transaction adding to actual usage int ts = currentTimeslot.getSerialNumber(); TariffSpecification spec1 = new TariffSpecification(brokerContext.getBroker(), PowerType.CONSUMPTION); CustomerInfo austin = new CustomerInfo("Austin", 3); TariffTransaction ttx1 = new TariffTransaction(brokerContext.getBroker(), ts, TariffTransaction.Type.CONSUME, spec1, austin, 3, -10, 1); marketManagerService.handleMessage(ttx1); double[] actualUsage = (double[]) ReflectionTestUtils.getField(marketManagerService, "actualUsage"); assertEquals("actualUsage[ts]", -10, actualUsage[ts], 1e-6); TariffTransaction ttx2 = new TariffTransaction(brokerContext.getBroker(), ts, TariffTransaction.Type.CONSUME, spec1, austin, 3, -5, 1); marketManagerService.handleMessage(ttx2); actualUsage = (double[]) ReflectionTestUtils.getField(marketManagerService, "actualUsage"); assertEquals("actualUsage[ts]", -15, actualUsage[ts], 1e-6); // test: recording predicted usage when(portfolioManagerService.collectUsage(any(int.class))).thenReturn(20.0); when(portfolioManagerService.collectShiftedUsage(any(int.class), any(int.class))).thenReturn(20.0); marketManagerService.activate(ts); int firstEnabled = enabledTimeslots.get(0).getSerialNumber(); int lastEnabled = enabledTimeslots.get(enabledTimeslots.size() - 1).getSerialNumber(); double[][] predictedUsage = (double[][]) ReflectionTestUtils.getField(marketManagerService, "predictedUsage"); assertEquals("predictedUsage[1][next]", -20, predictedUsage[0][firstEnabled], 1e-6); assertEquals("predictedUsage[1][next]", -20, predictedUsage[1][lastEnabled], 1e-6); } /** * NOTE: * If we don't want to assume a specific * method for accumulating bootstrap data, * we could test a record that is in the * length of the usage record, so each cell * gets only 1 data point. */ @Test public void testMarketBootstrapData() { initWithBootData(); // test MarketBootstrapData double[] assignedMwh = (double[]) ReflectionTestUtils.getField(marketManagerService, "marketMWh"); double[] assignedPayments = (double[]) ReflectionTestUtils.getField(marketManagerService, "marketPayments"); // construct expected - prices should be recorded as positive double[] expectedMwh = { 1 + 1, 2 + 2, 3 + 3 }; double[] expectedPayments = { 1 * 10 + 1 * 20, 2 * 10 + 2 * 20, 3 * 10 + 3 * 20 }; assertArrayEquals("MarketBootstrapData correct mwh data", expectedMwh, assignedMwh, 1e-6); assertArrayEquals("MarketBootstrapData correct Payments data", expectedPayments, assignedPayments, 1e-6); } /** * Testing the private method that updates * estimation of future costs (cost curves) */ @Test public void testUpdateMarketTracking() { marketManagerService.initialize(brokerContext); double executionMWh3 = 2 - 1; double executionPrice3 = 3 - 0.1; marketManagerService.updateMarketTracking(3, executionMWh3, executionPrice3); double executionMWh4 = 2; double executionPrice4 = 3; marketManagerService.updateMarketTracking(4, executionMWh4, executionPrice4); double executionMWh5 = 2 + 1; double executionPrice5 = 3 + 0.1; marketManagerService.updateMarketTracking(5, executionMWh5, executionPrice5); double[] assignedMwh = (double[]) ReflectionTestUtils.getField(marketManagerService, "marketMWh"); double[] assignedPayments = (double[]) ReflectionTestUtils.getField(marketManagerService, "marketPayments"); double[] expectedMwh = new double[3]; double[] expectedPayments = new double[3]; // NOTE: should have been 3=>0, 4=>1, 5=>2, // but since IGNORED_TIME_SLOTS == 1, // the mapping is 4=>0, 5=>1, 3=>2, i.e. the one here: expectedMwh[0] += executionMWh4; expectedMwh[1] += executionMWh5; expectedMwh[2] += executionMWh3; expectedPayments[0] += executionMWh4 * executionPrice4; expectedPayments[1] += executionMWh5 * executionPrice5; expectedPayments[2] += executionMWh3 * executionPrice3; assertArrayEquals("updateMarketTracking() correct mwh data", expectedMwh, assignedMwh, 1e-6); assertArrayEquals("updateMarketTracking() correct Payments data", expectedPayments, assignedPayments, 1e-6); } /** * Testing the private method that tracks * min/max trade prices */ @Test public void testMinMaxTradePrice() { marketManagerService.initialize(brokerContext); marketManagerService.updateLowHighTradePrices(-1); marketManagerService.updateLowHighTradePrices(0); marketManagerService.updateLowHighTradePrices(1); double maxTradePrice = (Double) ReflectionTestUtils.getField(marketManagerService, "maxTradePrice"); double minTradePrice = (Double) ReflectionTestUtils.getField(marketManagerService, "minTradePrice"); assertEquals("maxTradePrice", 1, maxTradePrice, 1e-6); assertEquals("minTradePrice", -1, minTradePrice, 1e-6); } /** * Testing the private method that learns * bid clearing distributions for the * bidding MDP */ @Test public void testRecordTradeResult() { marketManagerService.initialize(brokerContext); int tradeCreationTime = 1; double executionPrice = 2; double executionMWh = 3; marketManagerService.recordTradeResult(tradeCreationTime, 4, executionPrice, executionMWh); marketManagerService.recordTradeResult(tradeCreationTime, 5, executionPrice, executionMWh); marketManagerService.recordTradeResult(tradeCreationTime, 6, executionPrice, executionMWh); marketManagerService.recordTradeResult(tradeCreationTime, 10, executionPrice - 1, executionMWh); marketManagerService.recordTradeResult(tradeCreationTime, 10, executionPrice + 1, executionMWh); // test: trades are recorded @SuppressWarnings("unchecked") TreeMap<Integer, ArrayList<PriceMwhPair>> supportingBidGroups = (TreeMap<Integer, ArrayList<PriceMwhPair>>) ReflectionTestUtils .getField(marketManagerService, "supportingBidGroups"); assertEquals("supportingBidGroups.size", 4, supportingBidGroups.size()); assertEquals("supportingBidGroups.get(4).size", 1, supportingBidGroups.get(4).size()); assertEquals("supportingBidGroups.get(5).size", 1, supportingBidGroups.get(5).size()); assertEquals("supportingBidGroups.get(6).size", 1, supportingBidGroups.get(6).size()); assertEquals("supportingBidGroups.get(10).size", 2, supportingBidGroups.get(10).size()); assertEquals("supportingBidGroups(4,0).price", executionPrice, supportingBidGroups.get(4).get(0).getPricePerMwh(), 1e-6); assertEquals("supportingBidGroups(5,0).price", executionPrice, supportingBidGroups.get(5).get(0).getPricePerMwh(), 1e-6); assertEquals("supportingBidGroups(6,0).price", executionPrice, supportingBidGroups.get(6).get(0).getPricePerMwh(), 1e-6); assertEquals("supportingBidGroups(10,0).price is lowest", executionPrice - 1, supportingBidGroups.get(10).get(0).getPricePerMwh(), 1e-6); assertEquals("supportingBidGroups(10,1).price is higher", executionPrice + 1, supportingBidGroups.get(10).get(1).getPricePerMwh(), 1e-6); assertEquals("supportingBidGroups(4,0).mwh", executionMWh, supportingBidGroups.get(4).get(0).getMwh(), 1e-6); assertEquals("supportingBidGroups(5,0).mwh", executionMWh, supportingBidGroups.get(5).get(0).getMwh(), 1e-6); assertEquals("supportingBidGroups(6,0).mwh", executionMWh, supportingBidGroups.get(6).get(0).getMwh(), 1e-6); assertEquals("supportingBidGroups(10,0).mwh", executionMWh, supportingBidGroups.get(10).get(0).getMwh(), 1e-6); assertEquals("supportingBidGroups(10,1).mwh", executionMWh, supportingBidGroups.get(10).get(1).getMwh(), 1e-6); } /** * Here we only verify function calls - * each of the called functions is tested * separately */ @Test public void testClearedTrade() { // prepare spy marketManagerService.initialize(brokerContext); MarketMgrThatDoesntSend spyMktMgr = spy(marketManagerService); // prepare some trade data ClearedTrade trade; double executionMWh = 2; double executionPrice = 3; Instant now = new Instant(null); Instant tradeCreationTime = now.plus(TimeService.HOUR); // this probably doesn't work but what we care about is the next line when(timeslotRepo.getTimeslotIndex(tradeCreationTime)).thenReturn(1); // record trade twice: with and without useMtx trade = new ClearedTrade(4, executionMWh, executionPrice, tradeCreationTime); ReflectionTestUtils.setField(configuratorFactoryService, "useMtx", false); spyMktMgr.handleMessage(trade); ReflectionTestUtils.setField(configuratorFactoryService, "useMtx", true); spyMktMgr.handleMessage(trade); // verify calls: updateMarketTracking only called if useMtx == false //verify(mockMktMgr, times(2)).handleMessage(trade); verify(spyMktMgr, times(1)).updateMarketTracking(4, executionMWh, executionPrice); verify(spyMktMgr, times(2)).updateLowHighTradePrices(executionPrice); verify(spyMktMgr, times(2)).recordTradeResult(timeslotRepo.getTimeslotIndex(tradeCreationTime), 4, executionPrice, executionMWh); } /** * Here we only verify function calls - * called function(s) is tested separately */ @Test public void testMarketTransaction() { // prepare spy marketManagerService.initialize(brokerContext); MarketMgrThatDoesntSend spyMktMgr = spy(marketManagerService); // prepare a market transaction double executionMWh = 2; double executionPrice = 3; Instant now = new Instant(null); Instant tradeCreationTime = now.plus(TimeService.HOUR); // this probably doesn't work but what we care about is the next line when(timeslotRepo.getTimeslotIndex(tradeCreationTime)).thenReturn(1); // record trade twice: with and without useMtx MarketTransaction mtx = new MarketTransaction(thebroker, 1, 4, executionMWh, executionPrice); ReflectionTestUtils.setField(configuratorFactoryService, "useMtx", false); spyMktMgr.handleMessage(mtx); ReflectionTestUtils.setField(configuratorFactoryService, "useMtx", true); spyMktMgr.handleMessage(mtx); // verify calls: updateMarketTracking only called if useMtx == true verify(spyMktMgr, times(1)).updateMarketTracking(4, executionMWh, executionPrice); } /** * Test * TODO cover a few more cases */ @Test public void test_MeanMarketPrice() { initWithBootData(); double avgPricePerMWH = marketManagerService.getMeanMarketPricePerMWH(); assertEquals("correct value for market price/mWh", 15.0, avgPricePerMWH, 1e-6); double avgPricePerKWH = marketManagerService.getMeanMarketPricePerKWH(); assertEquals("correct value for market price/kWh", 0.015, avgPricePerKWH, 1e-6); ArrayRealVector avgPriceArray = marketManagerService.getMarketAvgPricesArrayKwh(); double[] expected = new double[] { 0.015, 0.015, 0.015 }; assertArrayEquals("correct value for avg-prices array", expected, avgPriceArray.toArray(), 1e-6); double expected0 = 0.015; double expected1 = 0.015; double expected2 = 0.015; assertEquals("correct avg price per slot0", expected0, marketManagerService.getMarketAvgPricePerSlotKWH(0), 1e-6); assertEquals("correct avg price per slot1", expected1, marketManagerService.getMarketAvgPricePerSlotKWH(1), 1e-6); assertEquals("correct avg price per slot2", expected2, marketManagerService.getMarketAvgPricePerSlotKWH(2), 1e-6); assertEquals("correct avg price per slot - wrapping index", expected0, marketManagerService.getMarketAvgPricePerSlotKWH(4), 1e-6); } /** * Test */ @Test public void test_getMarketPricePerKWHRecordStd() { initWithBootData(); double std = marketManagerService.getMarketPricePerKWHRecordStd(); // bias corrected std: sum the squared errors, divide by n-1, and take the // root, and divide by 1000 to get from mWh => kWh assertEquals("correct value for market price/kWh std", 0, std, 1e-6); //assertEquals("correct value for market price/kWh std", Math.sqrt(3 * Math.pow(2,2) / (3 - 1)) / 1000, std, 1e-6); } /** * correctess of whether we can run DP * this tests assumes that cleared trades * are handled correctly: this should be taken * care of in a test above * * Test */ @Test public void test_canRunDP() { // make sure all fields are in place marketManagerService.initialize(brokerContext); ClearedTrade trade; BalancingTransaction balanceTx; Instant now = new Instant(null); Instant tradeCreationTime = now.plus(364 * TimeService.HOUR); // this probably doesn't work but what we care about is the next line when(timeslotRepo.getTimeslotIndex(tradeCreationTime)).thenReturn(364); // add the 'enabled timeslots from setUp(), eventhough // they are not contiguous trade = new ClearedTrade(364, 0, 0, tradeCreationTime); marketManagerService.handleMessage(trade); trade = new ClearedTrade(365, 0, 0, tradeCreationTime); marketManagerService.handleMessage(trade); balanceTx = new BalancingTransaction(thebroker, 1, -1234, -1.234); marketManagerService.handleMessage(balanceTx); int backup = (Integer) ReflectionTestUtils.getField(configuratorFactoryService.CONSTANTS, "LARGE_ENOUGH_SAMPLE_FOR_MARKET_TRADES"); ReflectionTestUtils.setField(configuratorFactoryService.CONSTANTS, "LARGE_ENOUGH_SAMPLE_FOR_MARKET_TRADES", 1); assertEquals("canRunDP()", true, marketManagerService.canRunDP(currentTimeslot.getSerialNumber(), enabledTimeslots)); ReflectionTestUtils.setField(configuratorFactoryService.CONSTANTS, "LARGE_ENOUGH_SAMPLE_FOR_MARKET_TRADES", 2); assertEquals("canRunDP()", false, marketManagerService.canRunDP(currentTimeslot.getSerialNumber(), enabledTimeslots)); // restore value ReflectionTestUtils.setField(configuratorFactoryService.CONSTANTS, "LARGE_ENOUGH_SAMPLE_FOR_MARKET_TRADES", backup); } /** * Test */ @Test public void test_ChargeMwhPair() { MarketManagerService.ChargeMwhPair pair1 = marketManagerService.new ChargeMwhPair(1.0, -1.0); assertEquals("getCharge()", 1.0, pair1.getCharge(), 1e-6); assertEquals("getMwh()", -1.0, pair1.getMwh(), 1e-6); } /** * Test */ @Test public void test_RunDP2013() { // make sure all fields are in place marketManagerService.initialize(brokerContext); ClearedTrade trade; BalancingTransaction balanceTx; int currentTimeslotIndex = 363; // easy initialization, but not consistent with ts=363 Instant now = new Instant(null); Instant tradeCreationTime = now.plus(364 * TimeService.HOUR); // this probably doesn't work but what we care about is the next line // initialize with transactions from ts 364 when(timeslotRepo.getTimeslotIndex(tradeCreationTime)).thenReturn(364); // 15/Mwh for n+1 trade = new ClearedTrade(364, 1, 15, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); // 20/Mwh for n+2 trade = new ClearedTrade(365, 1, 20, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); //0.025/Kwh => 25/Mwh for balancing balanceTx = new BalancingTransaction(thebroker, 364, -1000, -25); // ts kwh charge marketManagerService.handleMessage(balanceTx); // initialize with transactions from ts 365 when(timeslotRepo.getTimeslotIndex(tradeCreationTime)).thenReturn(365); // 15/Mwh for n+1 trade = new ClearedTrade(365, 1, 10, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); // 20/Mwh for n+2 trade = new ClearedTrade(366, 1, 20, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); //0.025/Kwh => 25/Mwh for balancing balanceTx = new BalancingTransaction(thebroker, 365, -1000, -25); // ts kwh charge marketManagerService.handleMessage(balanceTx); // initialize with transactions from ts 366 when(timeslotRepo.getTimeslotIndex(tradeCreationTime)).thenReturn(366); // 15/Mwh for n+1 trade = new ClearedTrade(366, 2, 20, tradeCreationTime); // TODO still 10 but should fail test since doesn't do a weighted avg // ts mwh charge marketManagerService.handleMessage(trade); // 20/Mwh for n+2 trade = new ClearedTrade(367, 1, 10, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); //0.025/Kwh => 25/Mwh for balancing balanceTx = new BalancingTransaction(thebroker, 366, -2000, -20); // -10 // ts kwh charge marketManagerService.handleMessage(balanceTx); // Run DP double neededMwh = 10; // not used marketManagerService.runDP2013(neededMwh, currentTimeslotIndex); MarketManagerService.DPCache dpCache2013 = (MarketManagerService.DPCache) ReflectionTestUtils .getField(marketManagerService, "dpCache2013"); // examine results ArrayList<Double> stateValues = dpCache2013.getStateValues(); assertEquals("stateValues.size", 3, stateValues.size()); double step0Val = stateValues.get(0); // balancing double step1Val = stateValues.get(1); // n+1 auction double step2Val = stateValues.get(2); // n+2 auction // // expected results: // // (-25-25-20) / (-1mwh -1mwh -2mwh) = -17.5 assertEquals("step0 - balancing", -17.5, step0Val, 1e-6); // // 1/4 was cleared for 10, 1/4 for 15, 1/2 for 20 //(*)action -10: 1/4 x -10 + 3/4 x V(s_t+1) = 1/4 x -10 + 3/4 x -17.5 = -15.625 // action -15: 1/2 x -15 + 1/2 x V(s_t+1) = 1/2 x -15 + 1/2 x -17.5 = -16.25 // action -20: 1 x -20 + 0 x V(s_t+1) = -20 assertEquals("step1 - auction n+1", -15.625, step1Val, 1e-6); // // 1/3 was cleared for 10, 2/3 was cleared for 20 //(*)action -10: 1/3 x -10 + 2/3 x V(s_t+1) = 1/3 x -10 + 2/3 x -15.625 = -13.75 // action -20: 1 x -20 + 0 x V(s_t+1) = -20 assertEquals("step2 - auction n+2", -13.75, step2Val, 1e-6); ArrayList<Double> bestActions = dpCache2013.getBestActions(); assertEquals("bestActions.size", 3, bestActions.size()); assertEquals("bestActions(n+1)", -10, bestActions.get(1), 1e-6); assertEquals("getBestAction(n+1)", -10, dpCache2013.getBestAction(1), 1e-6); assertEquals("bestActions(n+2)", -10, bestActions.get(2), 1e-6); assertEquals("getBestAction(n+2)", -10, dpCache2013.getBestAction(2), 1e-6); assertEquals("current ts - valid", true, dpCache2013.isValid(currentTimeslotIndex)); assertEquals("next ts - not valid", false, dpCache2013.isValid(currentTimeslotIndex + 1)); marketManagerService.runDP2013(neededMwh, currentTimeslotIndex + 1); assertEquals("current ts - not valid", false, dpCache2013.isValid(currentTimeslotIndex)); assertEquals("next ts - valid", true, dpCache2013.isValid(currentTimeslotIndex + 1)); } /** * Test */ @Test public void test_RunDP2014() { /////////////////// // SETUP /////////////////// // make sure all fields are in place marketManagerService.initialize(brokerContext); ClearedTrade trade; BalancingTransaction balanceTx; // easy initialization, but not consistent with ts=363 Instant now = new Instant(null); Instant tradeCreationTime = now.plus(364 * TimeService.HOUR); // this probably doesn't work but what we care about is the next line // initialize with transactions from ts 364 when(timeslotRepo.getTimeslotIndex(tradeCreationTime)).thenReturn(364); // 15/Mwh for n-1 trade = new ClearedTrade(364, 1, 15, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); // 20/Mwh for n-2 trade = new ClearedTrade(365, 1, 20, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); //0.025/Kwh => 25/Mwh for balancing balanceTx = new BalancingTransaction(thebroker, 364, -1000, -25); // ts kwh charge marketManagerService.handleMessage(balanceTx); // initialize with transactions from ts 365 when(timeslotRepo.getTimeslotIndex(tradeCreationTime)).thenReturn(365); // 15/Mwh for n-1 trade = new ClearedTrade(365, 1, 10, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); // 20/Mwh for n-2 trade = new ClearedTrade(366, 1, 20, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); //0.025/Kwh => 25/Mwh for balancing balanceTx = new BalancingTransaction(thebroker, 365, -1000, -25); // ts kwh charge marketManagerService.handleMessage(balanceTx); // initialize with transactions from ts 366 when(timeslotRepo.getTimeslotIndex(tradeCreationTime)).thenReturn(366); // 15/Mwh for n-1 trade = new ClearedTrade(366, 2, 20, tradeCreationTime); // TODO still 10 but should fail test since doesn't do a weighted avg // ts mwh charge marketManagerService.handleMessage(trade); // 20/Mwh for n-2 trade = new ClearedTrade(367, 1, 10, tradeCreationTime); // ts mwh charge marketManagerService.handleMessage(trade); //0.025/Kwh => 25/Mwh for balancing balanceTx = new BalancingTransaction(thebroker, 366, -2000, -20); // -10 // ts kwh charge marketManagerService.handleMessage(balanceTx); /////////////////// // TEST DP RESULTS /////////////////// String msgSuffix = " no orderbooks"; // To remind: cleared trades data (price, mwh) is // state n-1: (10, 1), (15, 1), (20, 2) // state n-2: (10, 1), (20, 1), (20, 1) // DP for action from state 1 double neededMwh = 10; // not used int currentTimeslotIndex = 369; // should be after above tradeCreationTime's int targetTimeslotIndex = 370; DPResult dpResult = marketManagerService.runDP2014(targetTimeslotIndex, neededMwh, currentTimeslotIndex); // // examine results - state values // ArrayList<Double> stateValues = dpResult.getStateValues(); assertNotNull("stateValues not null", stateValues); assertEquals("stateValues.size", 2, stateValues.size()); double V_0 = stateValues.get(0); // balancing double V_1 = stateValues.get(1); // n-1 auction // // state 0: // (-25-25-20) / (-1mwh -1mwh -2mwh) = -17.5 assertEquals("A(1): V(0) - balancing" + msgSuffix, -17.5, V_0, 1e-6); // // state 1: // 1/4 was cleared for 10, 1/4 for 15, 1/2 for 20 // action 0: 0 x 0 + 1 x V(s_t+1) = 0 + 1 x -17.5 = -17.5 //(*)action -10: 1/4 x -10 + 3/4 x V(s_t+1) = 1/4 x -10 + 3/4 x -17.5 = -15.625 // action -15: 1/2 x -15 + 1/2 x V(s_t+1) = 1/2 x -15 + 1/2 x -17.5 = -16.25 // action -20: 1 x -20 + 0 x V(s_t+1) = -20 assertEquals("A(1): V(1) - auction n-1" + msgSuffix, -15.625, V_1, 1e-6); // // examine results - action values // ArrayList<Double> bestActions = dpResult.getBestActions(); assertEquals("A(1): bestActions.size" + msgSuffix, 2, bestActions.size()); assertEquals("A(1): bestActions(n-1)" + msgSuffix, -10, bestActions.get(1), 1e-6); // DP for action from state 2 neededMwh = 10; // not used currentTimeslotIndex = 369; // should be after above tradeCreationTime's targetTimeslotIndex = 371; dpResult = marketManagerService.runDP2014(targetTimeslotIndex, neededMwh, currentTimeslotIndex); // // examine results - state values // stateValues = dpResult.getStateValues(); assertNotNull("stateValues not null", stateValues); assertEquals("stateValues.size", 3, stateValues.size()); V_0 = stateValues.get(0); // balancing V_1 = stateValues.get(1); // n-1 auction double V_2 = stateValues.get(2); // n-2 auction // // expected results: // // state 0: // (-25-25-20) / (-1mwh -1mwh -2mwh) = -17.5 assertEquals("A(2): V(0) - balancing" + msgSuffix, -17.5, V_0, 1e-6); // // state 1: // 1/4 was cleared for 10, 1/4 for 15, 1/2 for 20 // action 0: 0 x 0 + 1 x V(s_t+1) = 0 + 1 x -17.5 = -17.5 //(*)action -10: 1/4 x -10 + 3/4 x V(s_t+1) = 1/4 x -10 + 3/4 x -17.5 = -15.625 // action -15: 1/2 x -15 + 1/2 x V(s_t+1) = 1/2 x -15 + 1/2 x -17.5 = -16.25 // action -20: 1 x -20 + 0 x V(s_t+1) = -20 assertEquals("A(2): V(1) - auction n-1" + msgSuffix, -15.625, V_1, 1e-6); // // state 2: // 1/3 was cleared for 10, 2/3 was cleared for 20 // action 0: 0 x 0 + 1 x V(s_t+1) = 0 + 1 x -15.625 = -15.625 //(*)action -10: 1/3 x -10 + 2/3 x V(s_t+1) = 1/3 x -10 + 2/3 x -15.625 = -13.75 // action -20: 1 x -20 + 0 x V(s_t+1) = -20 assertEquals("A(2): V(2) - auction n-2" + msgSuffix, -13.75, V_2, 1e-6); // // examine results - action values // bestActions = dpResult.getBestActions(); assertEquals("bestActions.size", 3, bestActions.size()); assertEquals("bestActions(n-1)", -10, bestActions.get(1), 1e-6); assertEquals("bestActions(n-2)", -10, bestActions.get(2), 1e-6); // test with orderbooks msgSuffix = " with orderbooks"; // To remind: cleared trades data (price, mwh) is // state n-1: (10, 1), (15, 1), (20, 2) // state n-2: (10, 1), (20, 1), (20, 1) // DP for action from state 1 neededMwh = 10; // not used currentTimeslotIndex = 369; // should be after above tradeCreationTime's targetTimeslotIndex = 370; // order book for 370 Orderbook o1 = new Orderbook(370, null, null); o1.addAsk(new OrderbookOrder(-1.0, 14.0)); marketManagerService.handleMessage(o1); // dpResult = marketManagerService.runDP2014(targetTimeslotIndex, neededMwh, currentTimeslotIndex); // // examine results - state values // stateValues = dpResult.getStateValues(); assertNotNull("stateValues not null", stateValues); assertEquals("stateValues.size", 2, stateValues.size()); V_0 = stateValues.get(0); // balancing V_1 = stateValues.get(1); // n-1 auction // // state 0: // (-25-25-20) / (-1mwh -1mwh -2mwh) = -17.5 assertEquals("A(1): V(0) - balancing" + msgSuffix, -17.5, V_0, 1e-6); // // state 1: // 1/3 was cleared for 15, 2/3 for 20 // action 0: 0 x 0 + 1 x V(s_t+1) = 0 + 1 x -17.5 = -17.5 //(x)action -10: //(*)action -15: 1/3 x -15 + 2/3 x V(s_t+1) = 1/3 x -15 + 2/3 x -17.5 = -16.6666666 // action -20: 1 x -20 + 0 x V(s_t+1) = -20 assertEquals("A(1): V(1) - auction n-1" + msgSuffix, -16.666666, V_1, 1e-6); // // examine results - action values // bestActions = dpResult.getBestActions(); assertEquals("bestActions.size", 2, bestActions.size()); assertEquals("bestActions(n-1)", -15, bestActions.get(1), 1e-6); // DP for action from state 2 neededMwh = 10; // not used currentTimeslotIndex = 369; // should be after above tradeCreationTime's targetTimeslotIndex = 371; // order book for 371 Orderbook o2 = new Orderbook(371, null, null); o2.addAsk(new OrderbookOrder(-1.0, 19.0)); marketManagerService.handleMessage(o2); // dpResult = marketManagerService.runDP2014(targetTimeslotIndex, neededMwh, currentTimeslotIndex); // // examine results - state values // stateValues = dpResult.getStateValues(); assertNotNull("stateValues not null", stateValues); assertEquals("stateValues.size", 3, stateValues.size()); V_0 = stateValues.get(0); // balancing V_1 = stateValues.get(1); // n-1 auction V_2 = stateValues.get(2); // n-2 auction // // expected results: // // state 0: // (-25-25-20) / (-1mwh -1mwh -2mwh) = -17.5 assertEquals("A(2): V(0) - balancing" + msgSuffix, -17.5, V_0, 1e-6); // // state 1: // all cleared for 20 //(*)action 0: 0 x 0 + 1 x V(s_t+1) = 0 + 1 x -17.5 = -17.5 //(x)action -10: //(x)action -15: // action -20: 1 x -20 + 0 x V(s_t+1) = -20 assertEquals("A(2): V(1) - auction n-1" + msgSuffix, -17.5, V_1, 1e-6); // // state 2: // all cleared for 20 //(*)action 0: 0 x 0 + 1 x V(s_t+1) = 0 + 1 x -17.5 = -17.5 //(x)action -10: // action -20: 1 x -20 + 0 x V(s_t+1) = -20 assertEquals("A(2): V(2) - auction n-2" + msgSuffix, -17.5, V_2, 1e-6); // // examine results - action values // bestActions = dpResult.getBestActions(); assertEquals("bestActions.size", 3, bestActions.size()); assertEquals("bestActions(n-1)", -0.0, bestActions.get(1), 1e-6); assertEquals("bestActions(n-2)", -0.0, bestActions.get(2), 1e-6); // test with orderbooks that eliminate all msgSuffix = " orderbooks eliminate all"; // To remind: cleared trades data (price, mwh) is // state n-1: (10, 1), (15, 1), (20, 2) // state n-2: (10, 1), (20, 1), (20, 1) // DP for action from state 2 - orderbook eliminates all neededMwh = 10; // not used currentTimeslotIndex = 370; // should be after above tradeCreationTime's targetTimeslotIndex = 372; // order book for 372 Orderbook o3 = new Orderbook(372, null, null); o3.addAsk(new OrderbookOrder(-1.0, 100.0)); marketManagerService.handleMessage(o3); // dpResult = marketManagerService.runDP2014(targetTimeslotIndex, neededMwh, currentTimeslotIndex); // // examine results - state values // stateValues = dpResult.getStateValues(); assertNotNull("stateValues not null", stateValues); assertEquals("stateValues.size", 3, stateValues.size()); V_0 = stateValues.get(0); // balancing V_1 = stateValues.get(1); // n-1 auction V_2 = stateValues.get(2); // n-2 auction // // expected results: // // state 0: // (-25-25-20) / (-1mwh -1mwh -2mwh) = -17.5 assertEquals("A(2): V(0) - balancing" + msgSuffix, -17.5, V_0, 1e-6); // // state 1: // all cleared for 20 //(*)action 0: 0 x 0 + 1 x V(s_t+1) = 0 + 1 x -17.5 = -17.5 //(x)action -10: //(x)action -15: //(x)action -20: assertEquals("A(2): V(1) - auction n-1" + msgSuffix, -17.5, V_1, 1e-6); // // state 2: // all cleared for 20 //(*)action 0: 0 x 0 + 1 x V(s_t+1) = 0 + 1 x -17.5 = -17.5 //(x)action -10: //(x)action -20: assertEquals("A(2): V(2) - auction n-2" + msgSuffix, -17.5, V_2, 1e-6); // // examine results - action values // bestActions = dpResult.getBestActions(); assertEquals("bestActions.size", 3, bestActions.size()); assertEquals("bestActions(n-1)", -0.0, bestActions.get(1), 1e-6); assertEquals("bestActions(n-2)", -0.0, bestActions.get(2), 1e-6); } /** * Test */ @Test public void test_meanClearingPrice() { ArrayList<PriceMwhPair> clearings = new ArrayList<PriceMwhPair>(); clearings.add(new PriceMwhPair(1.0, 123)); clearings.add(new PriceMwhPair(2.0, 234)); clearings.add(new PriceMwhPair(3.0, 345)); assertEquals("meanClearingBidPrice", 2.0, marketManagerService.meanClearingBidPrice(clearings), 1e-6); } /** * Test */ @Test public void test_timeslotIsEnabled() { assertFalse("currentTimeslot is not enabled", marketManagerService.timeslotIsEnabled(enabledTimeslots, currentTimeslot.getSerialNumber())); Timeslot t364 = new Timeslot(364, null); assertTrue("timeslot 364 is not enabled", marketManagerService.timeslotIsEnabled(enabledTimeslots, t364.getSerialNumber())); Timeslot t365 = new Timeslot(365, null); assertTrue("timeslot 365 is enabled", marketManagerService.timeslotIsEnabled(enabledTimeslots, t365.getSerialNumber())); Timeslot t366 = new Timeslot(366, null); assertFalse("timeslot 366 is enabled", marketManagerService.timeslotIsEnabled(enabledTimeslots, t366.getSerialNumber())); } }