AUCTION Clear auction based on OPF results (qty's and lambdas). [CO, CB] = AUCTION(OFFERS, BIDS, AUCTION_TYPE, LIMIT_PRC, GTEE_PRC) Clears a set of BIDS and OFFERS based on the results of an OPF, where the pricing is adjusted for network losses and binding constraints. The arguments OFFERS and BIDS are structs with the following fields: qty - m x n, offer/bid quantities, m offers/bids, n blocks prc - m x n, offer/bid prices lam - m x n, corresponding lambdas total_qty - m x 1, total quantity cleared for each offer/bid There are 8 types of auctions implemented, specified by AUCTION_TYPE. 0 - discriminative pricing (price equal to offer or bid) 1 - last accepted offer auction 2 - first rejected offer auction 3 - last accepted bid auction 4 - first rejected bid auction 5 - first price auction (marginal unit, offer or bid, sets the price) 6 - second price auction (if offer is marginal, price set by min(FRO,LAB), else max(FRB,LAO) 7 - split the difference pricing (set by last accepted offer & bid) 8 - LAO sets seller price, LAB sets buyer price Whether or not cleared offer (bid) prices are guaranteed to be greater (less) than or equal to the corresponding offer (bid) price is specified by a flag GTEE_PRC.offer (GTEE_PRC.bid). The default is value true. Offer/bid and cleared offer/bid min and max prices are specified in the LIMIT_PRC struct with the following fields: max_offer min_bid max_cleared_offer min_cleared_bid Offers (bids) above (below) max_offer (min_bid) are treated as withheld and cleared offer (bid) prices above (below) max_cleared_offer (min_cleared_bid) are clipped to max_cleared offer (min_cleared_bid) if given. All of these limit prices are ignored if the field is missing or is empty. See also RUNMARKET, SMARTMKT.
0001 function [co, cb] = auction(offers, bids, auction_type, limit_prc, gtee_prc) 0002 %AUCTION Clear auction based on OPF results (qty's and lambdas). 0003 % [CO, CB] = AUCTION(OFFERS, BIDS, AUCTION_TYPE, LIMIT_PRC, GTEE_PRC) 0004 % Clears a set of BIDS and OFFERS based on the results of an OPF, where the 0005 % pricing is adjusted for network losses and binding constraints. 0006 % The arguments OFFERS and BIDS are structs with the following fields: 0007 % qty - m x n, offer/bid quantities, m offers/bids, n blocks 0008 % prc - m x n, offer/bid prices 0009 % lam - m x n, corresponding lambdas 0010 % total_qty - m x 1, total quantity cleared for each offer/bid 0011 % 0012 % There are 8 types of auctions implemented, specified by AUCTION_TYPE. 0013 % 0014 % 0 - discriminative pricing (price equal to offer or bid) 0015 % 1 - last accepted offer auction 0016 % 2 - first rejected offer auction 0017 % 3 - last accepted bid auction 0018 % 4 - first rejected bid auction 0019 % 5 - first price auction (marginal unit, offer or bid, sets the price) 0020 % 6 - second price auction (if offer is marginal, price set by 0021 % min(FRO,LAB), else max(FRB,LAO) 0022 % 7 - split the difference pricing (set by last accepted offer & bid) 0023 % 8 - LAO sets seller price, LAB sets buyer price 0024 % 0025 % Whether or not cleared offer (bid) prices are guaranteed to be greater 0026 % (less) than or equal to the corresponding offer (bid) price is specified by 0027 % a flag GTEE_PRC.offer (GTEE_PRC.bid). The default is value true. 0028 % 0029 % Offer/bid and cleared offer/bid min and max prices are specified in the 0030 % LIMIT_PRC struct with the following fields: 0031 % max_offer 0032 % min_bid 0033 % max_cleared_offer 0034 % min_cleared_bid 0035 % Offers (bids) above (below) max_offer (min_bid) are treated as withheld 0036 % and cleared offer (bid) prices above (below) max_cleared_offer 0037 % (min_cleared_bid) are clipped to max_cleared offer (min_cleared_bid) if 0038 % given. All of these limit prices are ignored if the field is missing 0039 % or is empty. 0040 % 0041 % See also RUNMARKET, SMARTMKT. 0042 0043 % MATPOWER 0044 % Copyright (c) 1996-2015 by Power System Engineering Research Center (PSERC) 0045 % by Ray Zimmerman, PSERC Cornell 0046 % 0047 % $Id: auction.m 2644 2015-03-11 19:34:22Z ray $ 0048 % 0049 % This file is part of MATPOWER. 0050 % Covered by the 3-clause BSD License (see LICENSE file for details). 0051 % See http://www.pserc.cornell.edu/matpower/ for more info. 0052 0053 %%----- initialization ----- 0054 %% define named indices into data matrices 0055 [PQ, PV, REF, NONE, BUS_I, BUS_TYPE, PD, QD, GS, BS, BUS_AREA, VM, ... 0056 VA, BASE_KV, ZONE, VMAX, VMIN, LAM_P, LAM_Q, MU_VMAX, MU_VMIN] = idx_bus; 0057 [GEN_BUS, PG, QG, QMAX, QMIN, VG, MBASE, GEN_STATUS, PMAX, PMIN, ... 0058 MU_PMAX, MU_PMIN, MU_QMAX, MU_QMIN, PC1, PC2, QC1MIN, QC1MAX, ... 0059 QC2MIN, QC2MAX, RAMP_AGC, RAMP_10, RAMP_30, RAMP_Q, APF] = idx_gen; 0060 0061 %% initialize some stuff 0062 delta = 1e-3; %% prices smaller than this are not used to determine X 0063 zero_tol = 1e-5; 0064 % zero_tol = 0.1; %% fmincon is SO bad with prices that it is 0065 %% NOT recommended for use with auction.m 0066 big_num = 1e6; 0067 if isempty(bids) 0068 bids = struct( 'qty', [], ... 0069 'prc', [], ... 0070 'lam', [], ... 0071 'total_qty', [] ); 0072 end 0073 if nargin < 4 || isempty(limit_prc) 0074 limit_prc = struct( 'max_offer', [], 'min_bid', [], ... 0075 'max_cleared_offer', [], 'min_cleared_bid', [] ); 0076 else 0077 if ~isfield(limit_prc, 'max_offer'), limit_prc.max_offer = []; end 0078 if ~isfield(limit_prc, 'min_bid'), limit_prc.min_bid = []; end 0079 if ~isfield(limit_prc, 'max_cleared_offer'), limit_prc.max_cleared_offer = []; end 0080 if ~isfield(limit_prc, 'min_cleared_bid'), limit_prc.min_cleared_bid = []; end 0081 end 0082 if nargin < 5 || isempty(gtee_prc) 0083 gtee_prc = struct( 'offer', 1, 'bid', 1 ); 0084 else 0085 if ~isfield(gtee_prc, 'offer'), gtee_prc.offer = 1; end 0086 if ~isfield(gtee_prc, 'bid'), gtee_prc.bid = 1; end 0087 end 0088 0089 [nro, nco] = size(offers.qty); 0090 [nrb, ncb] = size(bids.qty); 0091 0092 %% determine cleared quantities 0093 if isempty(limit_prc.max_offer) 0094 [co.qty, o.on, o.off] = clear_qty(offers.qty, offers.total_qty); 0095 else 0096 mask = offers.prc <= limit_prc.max_offer; 0097 [co.qty, o.on, o.off] = clear_qty(offers.qty, offers.total_qty, mask); 0098 end 0099 if isempty(limit_prc.min_bid) 0100 [cb.qty, b.on, b.off] = clear_qty(bids.qty, bids.total_qty); 0101 else 0102 mask = bids.prc <= limit_prc.min_bid; 0103 [cb.qty, b.on, b.off] = clear_qty(bids.qty, bids.total_qty, mask); 0104 end 0105 0106 %% initialize cleared prices 0107 co.prc = zeros(nro, nco); %% cleared offer prices 0108 cb.prc = zeros(nrb, ncb); %% cleared bid prices 0109 0110 %%----- compute exchange rates to scale lam to get desired pricing ----- 0111 %% The locationally adjusted offer/bid price, when normalized to an arbitrary 0112 %% reference location where lambda is equal to ref_lam, is: 0113 %% norm_prc = prc * (ref_lam / lam) 0114 %% Then we can define the ratio between the normalized offer/bid prices 0115 %% and the ref_lam as an exchange rate X: 0116 %% X = norm_prc / ref_lam = prc / lam 0117 %% This X represents the ratio between the marginal unit (setting lambda) 0118 %% and the offer/bid price in question. 0119 0120 if auction_type == 0 || auction_type == 5 %% don't bother scaling anything 0121 X = struct( 'LAO', 1, ... 0122 'FRO', 1, ... 0123 'LAB', 1, ... 0124 'FRB', 1); 0125 else 0126 X = compute_exchange_rates(offers, bids, o, b); 0127 end 0128 0129 %% cleared offer/bid prices for different auction types 0130 if auction_type == 0 %% discriminative 0131 co.prc = offers.prc; 0132 cb.prc = bids.prc; 0133 elseif auction_type == 1 %% LAO 0134 co.prc = offers.lam * X.LAO; 0135 cb.prc = bids.lam * X.LAO; 0136 elseif auction_type == 2 %% FRO 0137 co.prc = offers.lam * X.FRO; 0138 cb.prc = bids.lam * X.FRO; 0139 elseif auction_type == 3 %% LAB 0140 co.prc = offers.lam * X.LAB; 0141 cb.prc = bids.lam * X.LAB; 0142 elseif auction_type == 4 %% FRB 0143 co.prc = offers.lam * X.FRB; 0144 cb.prc = bids.lam * X.FRB; 0145 elseif auction_type == 5 %% 1st price 0146 co.prc = offers.lam; 0147 cb.prc = bids.lam; 0148 elseif auction_type == 6 %% 2nd price 0149 if abs(1 - X.LAO) < zero_tol 0150 co.prc = offers.lam * min(X.FRO,X.LAB); 0151 cb.prc = bids.lam * min(X.FRO,X.LAB); 0152 else 0153 co.prc = offers.lam * max(X.LAO,X.FRB); 0154 cb.prc = bids.lam * max(X.LAO,X.FRB); 0155 end 0156 elseif auction_type == 7 %% split the difference 0157 co.prc = offers.lam * (X.LAO + X.LAB) / 2; 0158 cb.prc = bids.lam * (X.LAO + X.LAB) / 2; 0159 elseif auction_type == 8 %% LAO seller, LAB buyer 0160 co.prc = offers.lam * X.LAO; 0161 cb.prc = bids.lam * X.LAB; 0162 end 0163 0164 %% guarantee that cleared offer prices are >= offers 0165 if gtee_prc.offer 0166 clip = o.on .* (offers.prc - co.prc); 0167 co.prc = co.prc + (clip > zero_tol) .* clip; 0168 end 0169 0170 %% guarantee that cleared bid prices are <= bids 0171 if gtee_prc.bid 0172 clip = b.on .* (bids.prc - cb.prc); 0173 cb.prc = cb.prc + (clip < -zero_tol) .* clip; 0174 end 0175 0176 %% clip cleared offer prices by limit_prc.max_cleared_offer 0177 if ~isempty(limit_prc.max_cleared_offer) 0178 co.prc = co.prc + (co.prc > limit_prc.max_cleared_offer) .* ... 0179 (limit_prc.max_cleared_offer - co.prc); 0180 end 0181 0182 %% clip cleared bid prices by limit_prc.min_cleared_bid 0183 if ~isempty(limit_prc.min_cleared_bid) 0184 cb.prc = cb.prc + (cb.prc < limit_prc.min_cleared_bid) .* ... 0185 (limit_prc.min_cleared_bid - cb.prc); 0186 end 0187 0188 %% make prices uniform after clipping (except for discrim auction) 0189 %% since clipping may only affect a single block of a multi-block generator 0190 if auction_type ~= 0 0191 %% equal to largest price in row 0192 if nco > 1 0193 co.prc = diag(max(co.prc')) * ones(nro,nco); 0194 end 0195 if ncb > 1 0196 cb.prc = diag(min(cb.prc')) * ones(nrb,ncb); 0197 end 0198 end 0199 0200 0201 function X = compute_exchange_rates(offers, bids, o, b, delta) 0202 %COMPUTE_EXCHANGE_RATES Determine the scale factors for LAO, FRO, LAB, FRB 0203 % Inputs: 0204 % offers, bids (same as for auction) 0205 % o, b - structs with on, off fields, each same dim as qty field of offers 0206 % or bids, 1 if corresponding block is accepted, 0 otherwise 0207 % delta - optional prices smaller than this are not used to determine X 0208 % Outputs: 0209 % X - struct with fields LAO, FRO, LAB, FRB containing scale factors 0210 % to use for each type of auction 0211 0212 if nargin < 5 0213 delta = 1e-3; %% prices smaller than this are not used to determine X 0214 end 0215 zero_tol = 1e-5; 0216 0217 %% eliminate terms with lam < delta (X would not be accurate) 0218 olam = offers.lam; 0219 blam = bids.lam; 0220 olam(olam(:) < delta) = NaN; 0221 blam(blam(:) < delta) = NaN; 0222 0223 %% eliminate rows for 0 qty offers/bids 0224 [nro, nco] = size(offers.qty); 0225 [nrb, ncb] = size(bids.qty); 0226 omask = ones(nro,nco); 0227 if nco == 1 0228 temp = offers.qty; 0229 else 0230 temp = sum(offers.qty')'; 0231 end 0232 omask(temp == 0, :) = NaN; 0233 bmask = ones(nrb,ncb); 0234 if ncb == 1 0235 temp = bids.qty; 0236 else 0237 temp = sum(bids.qty')'; 0238 end 0239 bmask(temp == 0, :) = NaN; 0240 0241 %% by default, don't scale anything 0242 X.LAO = 1; 0243 X.FRO = 1; 0244 X.LAB = 1; 0245 X.FRB = 1; 0246 0247 %% don't scale if we have any negative lambdas or all are too close to 0 0248 if all(all(offers.lam > -zero_tol)) 0249 %% ratios 0250 Xo = omask .* offers.prc ./ olam; 0251 Xb = bmask .* bids.prc ./ blam; 0252 0253 %% exchange rate for LAO (X.LAO * lambda == LAO, for corresponding lambda) 0254 X.LAO = o.on .* Xo; 0255 X.LAO( o.off(:) ) = NaN; 0256 X.LAO( X.LAO(:) > 1+zero_tol ) = NaN; %% don't let gens @ Pmin set price 0257 X.LAO = max( X.LAO(:) ); 0258 0259 %% exchange rate for FRO (X.FRO * lambda == FRO, for corresponding lambda) 0260 X.FRO = o.off .* Xo; 0261 X.FRO( o.on(:) ) = NaN; 0262 X.FRO = min( X.FRO(:) ); 0263 0264 if nrb 0265 %% exchange rate for LAB (X.LAB * lambda == LAB, for corresponding lambda) 0266 X.LAB = b.on .* Xb; 0267 X.LAB( b.off(:) ) = NaN; 0268 X.LAB( X.LAB(:) < 1-zero_tol ) = NaN; %% don't let set price 0269 X.LAB = min( X.LAB(:) ); 0270 0271 %% exchange rate for FRB (X.FRB * lambda == FRB, for corresponding lambda) 0272 X.FRB = b.off .* Xb; 0273 X.FRB( b.on(:) ) = NaN; 0274 X.FRB = max( X.FRB(:) ); 0275 end 0276 end 0277 0278 0279 function [cqty, on, off] = clear_qty(qty, total_cqty, mask) 0280 %CLEAR_QTY Computed cleared offer/bid quantities from totals. 0281 % Inputs: 0282 % qty - m x n, offer/bid quantities, m offers/bids, n blocks 0283 % total_cqty - m x 1, total cleared quantity for each offer/bid 0284 % mask - m x n, boolean indicating which offers/bids are valid (not withheld) 0285 % Outputs: 0286 % cqty - m x n, cleared offer/bid quantities, m offers/bids, n blocks 0287 % on - m x n, 1 if partially or fully accepted, 0 if rejected 0288 % off - m x n, 1 if rejected, 0 if partially or fully accepted 0289 0290 [nr, nc] = size(qty); 0291 accept = zeros(nr,nc); 0292 cqty = zeros(nr,nc); 0293 for i = 1:nr %% offer/bid i 0294 for j = 1:nc %% block j 0295 if qty(i, j) %% ignore zero quantity offers/bids 0296 %% compute fraction of the block accepted ... 0297 accept(i, j) = (total_cqty(i) - sum(qty(i, 1:j-1))) / qty(i, j); 0298 %% ... clipped to the range [0, 1] (i.e. 0-100%) 0299 if accept(i, j) > 1 0300 accept(i, j) = 1; 0301 elseif accept(i, j) < 1.0e-5 0302 accept(i, j) = 0; 0303 end 0304 cqty(i, j) = qty(i, j) * accept(i, j); 0305 end 0306 end 0307 end 0308 0309 if nargin == 3 0310 accept = mask .* accept; 0311 end 0312 0313 on = (accept > 0); 0314 off = (accept == 0);