Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize auctions api #108

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.economicsl.auctions;


import org.economicsl.auctions.OrderTracker.*;
import org.economicsl.auctions.singleunit.OpenBidAuction;
import org.economicsl.auctions.singleunit.OrderGenerator;
import org.economicsl.auctions.singleunit.orders.Order;
import org.economicsl.auctions.participants.OrderGenerator;
import org.economicsl.auctions.participants.OrderTracker.*;
import org.economicsl.auctions.singleunit.OpenBidSingleUnitAuction;
import org.economicsl.auctions.singleunit.orders.SingleUnitOrder;
import org.economicsl.auctions.singleunit.pricing.MidPointPricingPolicy;

import scala.Option;
Expand All @@ -27,23 +27,23 @@ public static void main(String[] args) {
// define the auction mechanism...
TestStock googleStock = new TestStock();
MidPointPricingPolicy<TestStock> midpointPricingPolicy = new MidPointPricingPolicy<>();
OpenBidAuction<TestStock> doubleAuction = OpenBidAuction.withUniformClearingPolicy(midpointPricingPolicy, googleStock);
OpenBidSingleUnitAuction<TestStock> doubleAuction = OpenBidSingleUnitAuction.withUniformClearingPolicy(midpointPricingPolicy, googleStock);

// generate some random order flow...
int numberOrders = 10000;
Random prng = new Random(42);
Stream<Tuple2<UUID, Order<TestStock>>> orders = OrderGenerator.randomOrders(0.5, numberOrders, googleStock, prng);
Stream<Tuple2<UUID, SingleUnitOrder<TestStock>>> orders = OrderGenerator.randomOrders(0.5, numberOrders, googleStock, prng);

List<Either<Rejected, Accepted>> insertResults = new ArrayList<>();

for (Tuple2<UUID, Order<TestStock>> order:JavaConverters.seqAsJavaList(orders)) {
Tuple2<OpenBidAuction<TestStock>, Either<Rejected, Accepted>> insertResult = doubleAuction.insert(order);
for (Tuple2<UUID, SingleUnitOrder<TestStock>> order:JavaConverters.seqAsJavaList(orders)) {
Tuple2<OpenBidSingleUnitAuction<TestStock>, Either<Rejected, Accepted>> insertResult = doubleAuction.insert(order);
doubleAuction = insertResult._1();
insertResults.add(insertResult._2());
}

// clear the auction...
Tuple2<OpenBidAuction<TestStock>, Option<Stream<SpotContract>>> results = doubleAuction.clear();
Tuple2<OpenBidSingleUnitAuction<TestStock>, Option<Stream<SpotContract>>> results = doubleAuction.clear();
List<SpotContract> fills = JavaConverters.seqAsJavaList(results._2().get());

// print the results to console...
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package org.economicsl.auctions.singleunit

import org.economicsl.auctions.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.Token
import org.economicsl.auctions.singleunit.orders.Order
import org.economicsl.auctions.participants.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.participants.Token
import org.economicsl.auctions.singleunit.orders.SingleUnitOrder
import org.economicsl.core.Tradable


trait AuctionSimulation {

type InsertResult[T <: Tradable, A <: Auction[T, A]] = (A, Stream[Either[Rejected, Accepted]])
type InsertResult[T <: Tradable, A <: SingleUnitAuction[T, A]] = (A, Stream[Either[Rejected, Accepted]])

def insert[T <: Tradable, A <: Auction[T, A]](initial: A)(orders: Stream[(Token, Order[T])]): (A, Stream[Either[Rejected, Accepted]]) = {
def insert[T <: Tradable, A <: SingleUnitAuction[T, A]](initial: A)(orders: Stream[(Token, SingleUnitOrder[T])]): (A, Stream[Either[Rejected, Accepted]]) = {
orders.foldLeft((initial, Stream.empty[Either[Rejected, Accepted]])) {
case ((auction, insertResults), order) =>
val (updated, insertResult) = auction.insert(order)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ limitations under the License.
package org.economicsl.auctions.singleunit

import org.economicsl.auctions._
import org.economicsl.auctions.participants.{OrderGenerator, Token}
import org.economicsl.auctions.quotes._
import org.economicsl.auctions.singleunit.orders.Order
import org.economicsl.auctions.singleunit.orders.SingleUnitOrder
import org.economicsl.auctions.singleunit.pricing.MidPointPricingPolicy
import org.economicsl.core.{Price, Tradable}

Expand All @@ -33,15 +34,15 @@ object ContinuousDoubleAuction extends App {

val google: TestStock = TestStock()
val pricingRule = new MidPointPricingPolicy[TestStock]
val withDiscriminatoryPricing = OpenBidAuction.withDiscriminatoryClearingPolicy(pricingRule, google)
val withDiscriminatoryPricing = OpenBidSingleUnitAuction.withDiscriminatoryClearingPolicy(pricingRule, google)

// generate a very large stream of random orders...
type OrderFlow[T <: Tradable] = Stream[(Token, Order[T])]
type OrderFlow[T <: Tradable] = Stream[(Token, SingleUnitOrder[T])]
val prng = new Random(42)
val orders: OrderFlow[TestStock] = OrderGenerator.randomOrders(0.5)(1000000, google, prng)

// A lazy, tail-recursive implementation of a continuous double auction!
def continuous[T <: Tradable, A <: Auction[T, A]](auction: A)(incoming: OrderFlow[T]): Stream[(A, Option[Stream[SpotContract]])] = {
def continuous[T <: Tradable, A <: SingleUnitAuction[T, A]](auction: A)(incoming: OrderFlow[T]): Stream[(A, Option[Stream[SpotContract]])] = {
@annotation.tailrec
def loop(da: A, in: OrderFlow[T], out: Stream[(A, Option[Stream[SpotContract]])]): Stream[(A, Option[Stream[SpotContract]])] = in match {
case Stream.Empty => out
Expand All @@ -57,7 +58,7 @@ object ContinuousDoubleAuction extends App {
* containing the unmatched orders following each clear. Basically the entire auction history is stored in the
* stream of clear results.
*/
val results = continuous[TestStock, OpenBidAuction[TestStock]](withDiscriminatoryPricing)(orders)
val results = continuous[TestStock, OpenBidSingleUnitAuction[TestStock]](withDiscriminatoryPricing)(orders)

val prices: Stream[Price] = results.flatMap{ case (_, fills) =>
fills.flatMap(_.headOption).map(_.price)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.economicsl.auctions.quotes.AskPriceQuoteRequest
import org.economicsl.auctions.singleunit.orders.{LimitAskOrder, LimitBidOrder}
import org.economicsl.auctions.singleunit.pricing.AskQuotePricingPolicy
import org.economicsl.auctions._
import org.economicsl.auctions.participants.{OrderGenerator, Token, TokenGenerator}
import org.economicsl.core.Price
import org.scalatest.{FlatSpec, Matchers}

Expand All @@ -47,8 +48,8 @@ class FirstPriceOpenBidAuctionSpec
val (_, highestPricedBidOrder) = bidOrders.maxBy{ case (_, bidOrder) => bidOrder.limit }

// seller uses a first-priced, open bid auction...
val firstPriceOpenBidAuction: OpenBidAuction[ParkingSpace] = {
OpenBidAuction.withUniformClearingPolicy(AskQuotePricingPolicy[ParkingSpace], parkingSpace)
val firstPriceOpenBidAuction: OpenBidSingleUnitAuction[ParkingSpace] = {
OpenBidSingleUnitAuction.withUniformClearingPolicy(AskQuotePricingPolicy[ParkingSpace], parkingSpace)
}

// Seller that must sell at any positive price
Expand All @@ -58,7 +59,7 @@ class FirstPriceOpenBidAuctionSpec
val (withReservationAskOrder, _) = firstPriceOpenBidAuction.insert(reservation)

// withBidOrders will include all accepted bids (this is trivially parallel..)
val (withBidOrders, _) = insert[ParkingSpace, OpenBidAuction[ParkingSpace]](withReservationAskOrder)(bidOrders)
val (withBidOrders, _) = insert[ParkingSpace, OpenBidSingleUnitAuction[ParkingSpace]](withReservationAskOrder)(bidOrders)
val (clearedAuction, clearResults) = withBidOrders.clear

"A First-Price, Open-Bid Auction (FPOBA)" should "be able to process ask price quote requests" in {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ package org.economicsl.auctions.singleunit

import java.util.UUID

import org.economicsl.auctions.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.participants.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.singleunit.orders.{LimitAskOrder, LimitBidOrder}
import org.economicsl.auctions.singleunit.pricing.AskQuotePricingPolicy
import org.economicsl.auctions._
import org.economicsl.auctions.participants.{OrderGenerator, Token, TokenGenerator}
import org.economicsl.core.Price
import org.scalatest.{FlatSpec, Matchers}

Expand All @@ -40,8 +41,8 @@ class FirstPriceSealedBidAuctionSpec
// seller uses a first-priced, sealed bid auction...
val uuid: UUID = UUID.randomUUID()
val parkingSpace = ParkingSpace(uuid)
val firstPriceSealedBidAuction: SealedBidAuction[ParkingSpace] = {
SealedBidAuction.withUniformClearingPolicy(AskQuotePricingPolicy[ParkingSpace], parkingSpace)
val firstPriceSealedBidAuction: SealedBidSingleUnitAuction[ParkingSpace] = {
SealedBidSingleUnitAuction.withUniformClearingPolicy(AskQuotePricingPolicy[ParkingSpace], parkingSpace)
}

// suppose that seller must sell the parking space at any positive price...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ package org.economicsl.auctions.singleunit

import java.util.UUID

import org.economicsl.auctions.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.singleunit.orders.{BidOrder, LimitAskOrder, LimitBidOrder}
import org.economicsl.auctions.participants.{OrderGenerator, Token}
import org.economicsl.auctions.participants.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.singleunit.orders.{LimitAskOrder, LimitBidOrder, SingleUnitBidOrder}
import org.economicsl.auctions.singleunit.pricing.BidQuotePricingPolicy
import org.economicsl.auctions.{Issuer, Seller, Service, Token}
import org.economicsl.auctions.{Issuer, Seller, Service}
import org.economicsl.core.Price
import org.scalatest.{FlatSpec, Matchers}

Expand All @@ -38,14 +39,14 @@ class FirstPriceSealedBidReverseAuctionSpec

// reverse auction to procure a service at lowest possible cost...
val service = Service()
val firstPriceSealedBidReverseAuction: SealedBidAuction[Service] = {
SealedBidAuction.withUniformClearingPolicy(BidQuotePricingPolicy[Service], service)
val firstPriceSealedBidReverseAuction: SealedBidSingleUnitAuction[Service] = {
SealedBidSingleUnitAuction.withUniformClearingPolicy(BidQuotePricingPolicy[Service], service)
}

// buyer is willing to pay anything...
val buyer: Issuer = UUID.randomUUID()
val buyersToken: Token = UUID.randomUUID()
val reservationBidOrder: (Token, BidOrder[Service]) = (buyersToken, LimitBidOrder(buyer, Price.MaxValue, service))
val reservationBidOrder: (Token, SingleUnitBidOrder[Service]) = (buyersToken, LimitBidOrder(buyer, Price.MaxValue, service))
val (withReservationBidOrder, _) = firstPriceSealedBidReverseAuction.insert(reservationBidOrder)

// generate some random sellers...
Expand All @@ -55,7 +56,7 @@ class FirstPriceSealedBidReverseAuctionSpec
val (_, lowestPricedAskOrder): (Token, LimitAskOrder[Service]) = offers.minBy{ case (_, askOrder) => askOrder.limit }

// insert the ask orders into the auction mechanism...can be done in parallel!
val (withAskOrders, _): (SealedBidAuction[Service], Stream[Either[Rejected, Accepted]]) = {
val (withAskOrders, _): (SealedBidSingleUnitAuction[Service], Stream[Either[Rejected, Accepted]]) = {
offers.foldLeft((withReservationBidOrder, Stream.empty[Either[Rejected, Accepted]])) {
case ((auction, insertResults), askOrder) =>
val (updatedAuction, insertResult) = auction.insert(askOrder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ limitations under the License.
*/
package org.economicsl.auctions.singleunit

import org.economicsl.auctions.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.{TestStock, Token}
import org.economicsl.auctions.singleunit.orders.Order
import org.economicsl.auctions.participants.{OrderGenerator, Token}
import org.economicsl.auctions.participants.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.TestStock
import org.economicsl.auctions.singleunit.orders.SingleUnitOrder
import org.economicsl.auctions.singleunit.pricing.MidPointPricingPolicy
import org.scalatest.{FlatSpec, Matchers}

Expand All @@ -36,12 +37,12 @@ class PeriodicDoubleAuction
// generate a stream of random orders...
val google: TestStock = TestStock()
val prng = new Random(42)
val orders: Stream[(Token, Order[TestStock])] = OrderGenerator.randomOrders(0.5)(100, google, prng)
val orders: Stream[(Token, SingleUnitOrder[TestStock])] = OrderGenerator.randomOrders(0.5)(100, google, prng)

"A PeriodicDoubleAuction with uniform pricing" should "produce a single price at which all filled orders are processed." in {

val pricingRule = new MidPointPricingPolicy[TestStock]
val withUniformPricing = SealedBidAuction.withUniformClearingPolicy(pricingRule, google)
val withUniformPricing = SealedBidSingleUnitAuction.withUniformClearingPolicy(pricingRule, google)

// this whole process is data parallel...
val (withOrders, _) = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ package org.economicsl.auctions.singleunit

import java.util.UUID

import org.economicsl.auctions.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.participants.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions._
import org.economicsl.auctions.participants.{OrderGenerator, Token}
import org.economicsl.auctions.quotes.AskPriceQuoteRequest
import org.economicsl.auctions.singleunit.orders.{LimitAskOrder, LimitBidOrder}
import org.economicsl.auctions.singleunit.pricing.BidQuotePricingPolicy
Expand All @@ -41,8 +42,8 @@ class SecondPriceOpenBidAuctionSpec
val tickSize: Currency = 1
val uuid: UUID = UUID.randomUUID()
val parkingSpace = ParkingSpace(uuid)
val secondPriceOpenBidAuction: OpenBidAuction[ParkingSpace] = {
OpenBidAuction.withUniformClearingPolicy(BidQuotePricingPolicy[ParkingSpace], tickSize, parkingSpace)
val secondPriceOpenBidAuction: OpenBidSingleUnitAuction[ParkingSpace] = {
OpenBidSingleUnitAuction.withUniformClearingPolicy(BidQuotePricingPolicy[ParkingSpace], tickSize, parkingSpace)
}

val seller: UUID = UUID.randomUUID()
Expand All @@ -62,7 +63,7 @@ class SecondPriceOpenBidAuctionSpec
val (updatedAuction, result) = auction.insert(bidOrder)
(updatedAuction, result #:: results)
}
val (clearedAuction, fills): (OpenBidAuction[ParkingSpace], Option[Stream[SpotContract]]) = withBidOrders.clear
val (clearedAuction, fills): (OpenBidSingleUnitAuction[ParkingSpace], Option[Stream[SpotContract]]) = withBidOrders.clear

"A Second-Price, Open-Bid Auction (SPOBA)" should "be able to process ask price quote requests" in {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ package org.economicsl.auctions.singleunit

import java.util.UUID

import org.economicsl.auctions.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.participants.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions._
import org.economicsl.auctions.participants.{OrderGenerator, Token, TokenGenerator}
import org.economicsl.auctions.singleunit.orders.{LimitAskOrder, LimitBidOrder}
import org.economicsl.auctions.singleunit.pricing.BidQuotePricingPolicy
import org.economicsl.core.{Currency, Price}
Expand All @@ -41,8 +42,8 @@ class SecondPriceSealedBidAuctionSpec
val tickSize: Currency = 1
val uuid: UUID = UUID.randomUUID()
val parkingSpace = ParkingSpace(uuid)
val secondPriceSealedBidAuction: SealedBidAuction[ParkingSpace] = {
SealedBidAuction.withUniformClearingPolicy(BidQuotePricingPolicy[ParkingSpace], tickSize, parkingSpace)
val secondPriceSealedBidAuction: SealedBidSingleUnitAuction[ParkingSpace] = {
SealedBidSingleUnitAuction.withUniformClearingPolicy(BidQuotePricingPolicy[ParkingSpace], tickSize, parkingSpace)
}

val seller: UUID = UUID.randomUUID()
Expand All @@ -62,7 +63,7 @@ class SecondPriceSealedBidAuctionSpec

(updatedAuction, result #:: results)
}
val (clearedAuction, fills): (SealedBidAuction[ParkingSpace], Option[Stream[SpotContract]]) = withBidOrders.clear
val (clearedAuction, fills): (SealedBidSingleUnitAuction[ParkingSpace], Option[Stream[SpotContract]]) = withBidOrders.clear

"A Second-Price, Sealed-Bid Auction (SPSBA)" should "allocate the Tradable to the bidder that submitted the bid with the highest price." in {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ package org.economicsl.auctions.singleunit

import java.util.UUID


import org.economicsl.auctions.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.singleunit.orders.{BidOrder, LimitAskOrder, LimitBidOrder}
import org.economicsl.auctions.participants.{OrderGenerator, Token}
import org.economicsl.auctions.participants.OrderTracker.{Accepted, Rejected}
import org.economicsl.auctions.singleunit.orders.{LimitAskOrder, LimitBidOrder, SingleUnitBidOrder}
import org.economicsl.auctions.singleunit.pricing.AskQuotePricingPolicy
import org.economicsl.auctions.{Issuer, Seller, Service, Token}
import org.economicsl.auctions.{Issuer, Seller, Service}
import org.economicsl.core.Price
import org.scalatest.{FlatSpec, Matchers}

Expand All @@ -39,14 +39,14 @@ class SecondPriceSealedBidReverseAuctionSpec

// reverse auction to procure a service at lowest possible cost...
val service = Service()
val secondPriceSealedBidReverseAuction: SealedBidAuction[Service] = {
SealedBidAuction.withUniformClearingPolicy(AskQuotePricingPolicy[Service], service)
val secondPriceSealedBidReverseAuction: SealedBidSingleUnitAuction[Service] = {
SealedBidSingleUnitAuction.withUniformClearingPolicy(AskQuotePricingPolicy[Service], service)
}

// buyer is willing to pay anything...
val buyer: Issuer = UUID.randomUUID()
val buyersToken: Token = UUID.randomUUID()
val reservationBidOrder: (Token, BidOrder[Service]) = (buyersToken, LimitBidOrder(buyer, Price.MaxValue, service))
val reservationBidOrder: (Token, SingleUnitBidOrder[Service]) = (buyersToken, LimitBidOrder(buyer, Price.MaxValue, service))
val (withReservationBidOrder, _) = secondPriceSealedBidReverseAuction.insert(reservationBidOrder)

// generate some random sellers...
Expand All @@ -56,7 +56,7 @@ class SecondPriceSealedBidReverseAuctionSpec
val (_, lowestPricedAskOrder): (Token, LimitAskOrder[Service]) = offers.minBy{ case (_, order) => order.limit }

// insert the ask orders into the auction mechanism...can be done in parallel!
val (withAskOrders, _): (SealedBidAuction[Service], Stream[Either[Rejected, Accepted]]) = {
val (withAskOrders, _): (SealedBidSingleUnitAuction[Service], Stream[Either[Rejected, Accepted]]) = {
offers.foldLeft((withReservationBidOrder, Stream.empty[Either[Rejected, Accepted]])) {
case ((auction, insertResults), askOrder) =>
val (updatedAuction, insertResult) = auction.insert(askOrder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package org.economicsl.auctions.singleunit;


import org.economicsl.auctions.OrderTracker.*;
import org.economicsl.auctions.participants.OrderTracker.*;
import scala.Option;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package org.economicsl.auctions.singleunit;


import org.economicsl.auctions.OrderTracker.*;
import org.economicsl.auctions.participants.OrderTracker.*;
import scala.util.Either;


Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.economicsl.auctions.singleunit;


import org.economicsl.auctions.singleunit.orders.Order;
import org.economicsl.auctions.singleunit.orders.SingleUnitOrder;
import org.economicsl.auctions.singleunit.pricing.PricingPolicy;
import org.economicsl.core.Tradable;
import scala.Tuple2;
Expand Down Expand Up @@ -31,7 +31,7 @@ public abstract class JAuction<T extends Tradable, A extends JAuction<T, A>> {
* @return
* @todo get rid of Tuple2 class!
*/
public abstract InsertResult<A> insert(Tuple2<UUID, Order<T>> order);
public abstract InsertResult<A> insert(Tuple2<UUID, SingleUnitOrder<T>> order);

/** Returns an auction of type `A` with a particular pricing policy. */
public abstract A withPricingPolicy(PricingPolicy<T> updated);
Expand Down