From 5106b409777a7b7807380a6ee371c1648be057de Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 1 Mar 2017 10:49:13 +0300 Subject: [PATCH 01/19] Added a simple Fill case class. --- src/main/scala/org/economicsl/auctions/sandbox.sc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/economicsl/auctions/sandbox.sc b/src/main/scala/org/economicsl/auctions/sandbox.sc index 8418885..5191c24 100644 --- a/src/main/scala/org/economicsl/auctions/sandbox.sc +++ b/src/main/scala/org/economicsl/auctions/sandbox.sc @@ -58,13 +58,19 @@ case class WeightedAveragePricing(weight: Double) extends DiscriminatoryPricingR } +case class Fill(askOrder: LimitAskOrder, bidOrder: LimitBidOrder, price: Price) { + + val quantity: Quantity = Quantity(math.min(askOrder.quantity.value, bidOrder.quantity.value)) + +} + // example of buyer's bid (or M+1 price rule)...incentive compatible for the seller! -pairedOrders.map(WeightedAveragePricing(1.0)).toList +pairedOrders map { case (askOrder, bidOrder) => Fill(askOrder, bidOrder, WeightedAveragePricing(1.0)((askOrder, bidOrder))) } // example of seller's ask (or M price rule)...incentive compatible for the buyer -pairedOrders.map(WeightedAveragePricing(0.0)).toList +pairedOrders map { case (askOrder, bidOrder) => Fill(askOrder, bidOrder, WeightedAveragePricing(0.0)((askOrder, bidOrder))) } // split the trade surplus evenly...not incentive compatible! -pairedOrders.map(WeightedAveragePricing(0.5)).toList \ No newline at end of file +pairedOrders map { case (askOrder, bidOrder) => Fill(askOrder, bidOrder, WeightedAveragePricing(0.5)((askOrder, bidOrder))) } \ No newline at end of file From 167c56690c716784343764ff031c2a6dfc932cd8 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 1 Mar 2017 11:35:48 +0300 Subject: [PATCH 02/19] Added a quick interface for a double auction. --- .../economicsl/auctions/DoubleAuction.scala | 31 +++++++++++++++++++ .../scala/org/economicsl/auctions/Fill.scala | 23 ++++++++++++++ .../scala/org/economicsl/auctions/sandbox.sc | 6 ---- 3 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 src/main/scala/org/economicsl/auctions/DoubleAuction.scala create mode 100644 src/main/scala/org/economicsl/auctions/Fill.scala diff --git a/src/main/scala/org/economicsl/auctions/DoubleAuction.scala b/src/main/scala/org/economicsl/auctions/DoubleAuction.scala new file mode 100644 index 0000000..34bab3f --- /dev/null +++ b/src/main/scala/org/economicsl/auctions/DoubleAuction.scala @@ -0,0 +1,31 @@ +/* +Copyright 2017 EconomicSL + +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 org.economicsl.auctions + + +trait DoubleAuction { + + def insert(order: LimitAskOrder): Unit + + def insert(order: LimitBidOrder): Unit + + def remove(order: LimitAskOrder): Boolean + + def remove(order: LimitBidOrder): Boolean + + def clear: Stream[Fill] + +} diff --git a/src/main/scala/org/economicsl/auctions/Fill.scala b/src/main/scala/org/economicsl/auctions/Fill.scala new file mode 100644 index 0000000..2fd4449 --- /dev/null +++ b/src/main/scala/org/economicsl/auctions/Fill.scala @@ -0,0 +1,23 @@ +/* +Copyright 2017 EconomicSL + +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 org.economicsl.auctions + + +case class Fill(askOrder: LimitAskOrder, bidOrder: LimitBidOrder, price: Price) { + + val quantity: Quantity = Quantity(math.min(askOrder.quantity.value, bidOrder.quantity.value)) + +} diff --git a/src/main/scala/org/economicsl/auctions/sandbox.sc b/src/main/scala/org/economicsl/auctions/sandbox.sc index 5191c24..a56aed4 100644 --- a/src/main/scala/org/economicsl/auctions/sandbox.sc +++ b/src/main/scala/org/economicsl/auctions/sandbox.sc @@ -58,12 +58,6 @@ case class WeightedAveragePricing(weight: Double) extends DiscriminatoryPricingR } -case class Fill(askOrder: LimitAskOrder, bidOrder: LimitBidOrder, price: Price) { - - val quantity: Quantity = Quantity(math.min(askOrder.quantity.value, bidOrder.quantity.value)) - -} - // example of buyer's bid (or M+1 price rule)...incentive compatible for the seller! pairedOrders map { case (askOrder, bidOrder) => Fill(askOrder, bidOrder, WeightedAveragePricing(1.0)((askOrder, bidOrder))) } From 479db6b19615fb05368c10ee9a3defb5d94a4501 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 1 Mar 2017 13:40:58 +0300 Subject: [PATCH 03/19] Sketch implementation of clear method. --- .../scala/org/economicsl/auctions/DoubleAuction.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/economicsl/auctions/DoubleAuction.scala b/src/main/scala/org/economicsl/auctions/DoubleAuction.scala index 34bab3f..9d49bf0 100644 --- a/src/main/scala/org/economicsl/auctions/DoubleAuction.scala +++ b/src/main/scala/org/economicsl/auctions/DoubleAuction.scala @@ -15,6 +15,8 @@ limitations under the License. */ package org.economicsl.auctions +import org.economicsl.auctions.orderbooks.FourHeapOrderBook + trait DoubleAuction { @@ -26,6 +28,11 @@ trait DoubleAuction { def remove(order: LimitBidOrder): Boolean - def clear: Stream[Fill] + def clear: Stream[Fill] = { + val (pairedOrders, _) = orderBook.takeWhileMatched + pairedOrders.map{ case (a, b) => Fill(a, b, p((a, b))) } + } + + def orderBook: FourHeapOrderBook[LimitAskOrder, LimitBidOrder] } From bf2ae623368eda8be9754bb22866c4303c8a5bb1 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Tue, 14 Mar 2017 16:04:50 +0300 Subject: [PATCH 04/19] Started a draft implementation of a fill function. --- .../scala/org/economicsl/auctions/sandbox.sc | 16 ++++++------ .../economicsl/auctions/singleunit/Fill.scala | 25 +++++++++++++++++++ .../orderbooks/FourHeapOrderBook.scala | 2 +- 3 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 src/main/scala/org/economicsl/auctions/singleunit/Fill.scala diff --git a/src/main/scala/org/economicsl/auctions/sandbox.sc b/src/main/scala/org/economicsl/auctions/sandbox.sc index 2881c39..1699f29 100644 --- a/src/main/scala/org/economicsl/auctions/sandbox.sc +++ b/src/main/scala/org/economicsl/auctions/sandbox.sc @@ -1,9 +1,9 @@ import java.util.UUID import org.economicsl.auctions._ -import org.economicsl.auctions.singleunit.{LimitAskOrder, LimitBidOrder} +import org.economicsl.auctions.singleunit.Fill import org.economicsl.auctions.singleunit.orderbooks.FourHeapOrderBook -import org.economicsl.auctions.singleunit.pricing.{BuyersBidPricingRule, SellersAskPricingRule, WeightedAveragePricingRule} +import org.economicsl.auctions.singleunit.pricing.{BuyersBidPricingRule, PricingRule, SellersAskPricingRule, WeightedAveragePricingRule} /** Example `Tradable` object. */ @@ -62,13 +62,6 @@ val askPriceQuote = buyersBidPricing(orderBook5) val sellersAskPricing = new SellersAskPricingRule[Google]() val bidPriceQuote = sellersAskPricing(orderBook5) - -case class Fill[T <: Tradable](askOrder: LimitAskOrder[T], bidOrder: LimitBidOrder[T], price: Price) { - - val quantity: Quantity = Quantity(math.min(askOrder.quantity.value, bidOrder.quantity.value)) - -} - // example of a uniform price auction that would be incentive compatible for the sellers... val averagePricing = WeightedAveragePricingRule[Google](0.5) val averagePrice = averagePricing(orderBook5) @@ -76,3 +69,8 @@ val averagePrice = averagePricing(orderBook5) // take a look at paired orders val (pairedOrders, _) = orderBook5.takeWhileMatched pairedOrders.toList + + +def fill[T <: Tradable](pricingRule: PricingRule[T, Price])(orderBook: FourHeapOrderBook[T]): Option[(Fill[T], FourHeapOrderBook[T])] = { + pricingRule(orderBook).map{ price => val (askOrder, bidOrder) = orderBook.head; Some(Fill(askOrder, bidOrder, price), orderBook.tail) } +} \ No newline at end of file diff --git a/src/main/scala/org/economicsl/auctions/singleunit/Fill.scala b/src/main/scala/org/economicsl/auctions/singleunit/Fill.scala new file mode 100644 index 0000000..a019420 --- /dev/null +++ b/src/main/scala/org/economicsl/auctions/singleunit/Fill.scala @@ -0,0 +1,25 @@ +/* +Copyright 2017 EconomicSL + +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 org.economicsl.auctions.singleunit + +import org.economicsl.auctions.{Price, Quantity, Tradable} + + +case class Fill[T <: Tradable](askOrder: LimitAskOrder[T], bidOrder: LimitBidOrder[T], price: Price) { + + val quantity: Quantity = Quantity(math.min(askOrder.quantity.value, bidOrder.quantity.value)) + +} diff --git a/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/FourHeapOrderBook.scala b/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/FourHeapOrderBook.scala index 2a0e3eb..133f9f0 100644 --- a/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/FourHeapOrderBook.scala +++ b/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/FourHeapOrderBook.scala @@ -19,7 +19,7 @@ import org.economicsl.auctions.{Price, Tradable} import org.economicsl.auctions.singleunit.{LimitAskOrder, LimitBidOrder} -class FourHeapOrderBook[T <: Tradable] private(val matchedOrders: MatchedOrders[T], unMatchedOrders: UnMatchedOrders[T]) { +class FourHeapOrderBook[T <: Tradable] private(matchedOrders: MatchedOrders[T], unMatchedOrders: UnMatchedOrders[T]) { // value of lowest matched bid must exceed value of highest unmatched bid! require(matchedOrders.bidOrders.headOption.forall(b1 => unMatchedOrders.bidOrders.headOption.forall(b2 => b1.value >= b2.value))) From 5404ac87422163c0a63e7754adb5de28709bc7c9 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Tue, 14 Mar 2017 16:59:10 +0300 Subject: [PATCH 05/19] Updated auction interface. --- .../Auction.scala} | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) rename src/main/scala/org/economicsl/auctions/{DoubleAuction.scala => singleunit/Auction.scala} (53%) diff --git a/src/main/scala/org/economicsl/auctions/DoubleAuction.scala b/src/main/scala/org/economicsl/auctions/singleunit/Auction.scala similarity index 53% rename from src/main/scala/org/economicsl/auctions/DoubleAuction.scala rename to src/main/scala/org/economicsl/auctions/singleunit/Auction.scala index 9d49bf0..510a971 100644 --- a/src/main/scala/org/economicsl/auctions/DoubleAuction.scala +++ b/src/main/scala/org/economicsl/auctions/singleunit/Auction.scala @@ -13,26 +13,22 @@ 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 org.economicsl.auctions +package org.economicsl.auctions.singleunit -import org.economicsl.auctions.orderbooks.FourHeapOrderBook +import org.economicsl.auctions.Tradable -trait DoubleAuction { +/** Base trait for all single-unit auctions. */ +trait Auction[T <: Tradable, A <: Auction[T, A]] { - def insert(order: LimitAskOrder): Unit + def insert(order: LimitAskOrder[T]): A - def insert(order: LimitBidOrder): Unit + def insert(order: LimitBidOrder[T]): A - def remove(order: LimitAskOrder): Boolean + def remove(order: LimitAskOrder[T]): A - def remove(order: LimitBidOrder): Boolean + def remove(order: LimitBidOrder[T]): A - def clear: Stream[Fill] = { - val (pairedOrders, _) = orderBook.takeWhileMatched - pairedOrders.map{ case (a, b) => Fill(a, b, p((a, b))) } - } - - def orderBook: FourHeapOrderBook[LimitAskOrder, LimitBidOrder] + def clear: Stream[Fill[T]] } From c3979ef4a2cd16472f911e4204ea6cca8e9b8e59 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 15 Mar 2017 16:05:22 +0300 Subject: [PATCH 06/19] Added an implementation of DoubleAuction for discussion. --- .../auctions/singleunit/Auction.scala | 34 ----------- .../auctions/singleunit/DoubleAuction.scala | 60 +++++++++++++++++++ 2 files changed, 60 insertions(+), 34 deletions(-) delete mode 100644 src/main/scala/org/economicsl/auctions/singleunit/Auction.scala create mode 100644 src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala diff --git a/src/main/scala/org/economicsl/auctions/singleunit/Auction.scala b/src/main/scala/org/economicsl/auctions/singleunit/Auction.scala deleted file mode 100644 index 510a971..0000000 --- a/src/main/scala/org/economicsl/auctions/singleunit/Auction.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2017 EconomicSL - -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 org.economicsl.auctions.singleunit - -import org.economicsl.auctions.Tradable - - -/** Base trait for all single-unit auctions. */ -trait Auction[T <: Tradable, A <: Auction[T, A]] { - - def insert(order: LimitAskOrder[T]): A - - def insert(order: LimitBidOrder[T]): A - - def remove(order: LimitAskOrder[T]): A - - def remove(order: LimitBidOrder[T]): A - - def clear: Stream[Fill[T]] - -} diff --git a/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala b/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala new file mode 100644 index 0000000..73bf831 --- /dev/null +++ b/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala @@ -0,0 +1,60 @@ +/* +Copyright 2017 EconomicSL + +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 org.economicsl.auctions.singleunit + +import org.economicsl.auctions.{Price, Tradable} +import org.economicsl.auctions.singleunit.orderbooks.FourHeapOrderBook +import org.economicsl.auctions.singleunit.pricing.PricingRule + + +class DoubleAuction[T <: Tradable] private(orderBook: FourHeapOrderBook[T]) { + + def insert(order: LimitAskOrder[T]): DoubleAuction[T] = { + new DoubleAuction(orderBook + order) + } + + def insert(order: LimitBidOrder[T]): DoubleAuction[T] = { + new DoubleAuction(orderBook + order) + } + + def remove(order: LimitAskOrder[T]): DoubleAuction[T] = { + new DoubleAuction(orderBook - order) + } + + def remove(order: LimitBidOrder[T]): DoubleAuction[T] = { + new DoubleAuction(orderBook - order) + } + + def clear(p: PricingRule[T, Price]): (Option[Stream[Fill[T]]], DoubleAuction[T]) = { + p(orderBook) match { + case Some(price) => + val (pairedOrders, newOrderBook) = orderBook.takeWhileMatched + val fills = pairedOrders.map { case (askOrder, bidOrder) => Fill(askOrder, bidOrder, price) } + (Some(fills), new DoubleAuction(newOrderBook)) + case None => (None, new DoubleAuction(orderBook)) + } + } + +} + + +object DoubleAuction { + + def withEmptyOrderBook[T <: Tradable]: DoubleAuction[T] = { + new DoubleAuction(FourHeapOrderBook.empty[T]) + } + +} From e1b448cf24ae5e18d9708f9c734e54b8e9141e23 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 15 Mar 2017 16:14:45 +0300 Subject: [PATCH 07/19] Added some examples of using the immutable auction. --- .../scala/org/economicsl/auctions/sandbox.sc | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/economicsl/auctions/sandbox.sc b/src/main/scala/org/economicsl/auctions/sandbox.sc index f04f2e6..ba0ad95 100644 --- a/src/main/scala/org/economicsl/auctions/sandbox.sc +++ b/src/main/scala/org/economicsl/auctions/sandbox.sc @@ -1,7 +1,7 @@ import java.util.UUID import org.economicsl.auctions._ -import org.economicsl.auctions.singleunit.Fill +import org.economicsl.auctions.singleunit.{DoubleAuction, Fill} import org.economicsl.auctions.singleunit.orderbooks.FourHeapOrderBook import org.economicsl.auctions.singleunit.pricing._ @@ -74,6 +74,19 @@ val averagePrice = averagePricing(orderBook5) val (pairedOrders, _) = orderBook5.takeWhileMatched pairedOrders.toList -def fill[T <: Tradable](pricingRule: PricingRule[T, Price])(orderBook: FourHeapOrderBook[T]): Option[(Fill[T], FourHeapOrderBook[T])] = { - pricingRule(orderBook).map{ price => val (askOrder, bidOrder) = orderBook.head; Some(Fill(askOrder, bidOrder, price), orderBook.tail) } -} \ No newline at end of file +//def fill[T <: Tradable](pricingRule: PricingRule[T, Price])(orderBook: FourHeapOrderBook[T]): Option[(Fill[T], FourHeapOrderBook[T])] = { +// pricingRule(orderBook).map{ price => val (askOrder, bidOrder) = orderBook.head; Some(Fill(askOrder, bidOrder, price), orderBook.tail) } +//} + +// example usage of the auction... +val auction = DoubleAuction.withEmptyOrderBook[Google] +val auction2 = auction.insert(order4) +val auction3 = auction2.insert(order9) + +// thanks to @bherd-rb we can do things like this... +val (result, _) = auction3.clear(midPointPricing) +result.map(fills => fills.toList) + +// ...trivial to re-run the same auction with a different pricing rule! +val (result2, _) = auction3.clear(askQuotePricing) +result2.map(fills => fills.toList) \ No newline at end of file From 048e78e9ed7f9424eaad74fc44c9dfd4c248e9ea Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 15 Mar 2017 16:39:24 +0300 Subject: [PATCH 08/19] removed a bit of cruft from the worksheet. --- src/main/scala/org/economicsl/auctions/sandbox.sc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/scala/org/economicsl/auctions/sandbox.sc b/src/main/scala/org/economicsl/auctions/sandbox.sc index ba0ad95..e16e6c2 100644 --- a/src/main/scala/org/economicsl/auctions/sandbox.sc +++ b/src/main/scala/org/economicsl/auctions/sandbox.sc @@ -1,7 +1,7 @@ import java.util.UUID import org.economicsl.auctions._ -import org.economicsl.auctions.singleunit.{DoubleAuction, Fill} +import org.economicsl.auctions.singleunit.DoubleAuction import org.economicsl.auctions.singleunit.orderbooks.FourHeapOrderBook import org.economicsl.auctions.singleunit.pricing._ @@ -74,10 +74,6 @@ val averagePrice = averagePricing(orderBook5) val (pairedOrders, _) = orderBook5.takeWhileMatched pairedOrders.toList -//def fill[T <: Tradable](pricingRule: PricingRule[T, Price])(orderBook: FourHeapOrderBook[T]): Option[(Fill[T], FourHeapOrderBook[T])] = { -// pricingRule(orderBook).map{ price => val (askOrder, bidOrder) = orderBook.head; Some(Fill(askOrder, bidOrder, price), orderBook.tail) } -//} - // example usage of the auction... val auction = DoubleAuction.withEmptyOrderBook[Google] val auction2 = auction.insert(order4) From 0b4efd4d351d8d24d2d1697874e57b00bffaada5 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Thu, 16 Mar 2017 12:27:36 +0300 Subject: [PATCH 09/19] Added the implicits. --- .../org/economicsl/auctions/singleunit/DoubleAuction.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala b/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala index 73bf831..8fff479 100644 --- a/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala +++ b/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala @@ -53,8 +53,8 @@ class DoubleAuction[T <: Tradable] private(orderBook: FourHeapOrderBook[T]) { object DoubleAuction { - def withEmptyOrderBook[T <: Tradable]: DoubleAuction[T] = { - new DoubleAuction(FourHeapOrderBook.empty[T]) + def withEmptyOrderBook[T <: Tradable](implicit askOrdering: Ordering[LimitAskOrder[T]], bidOrdering: Ordering[LimitBidOrder[T]]): DoubleAuction[T] = { + new DoubleAuction(FourHeapOrderBook.empty[T](askOrdering, bidOrdering)) } } From 5a138f62de500e9f47098ab81b98627c056d73d9 Mon Sep 17 00:00:00 2001 From: Benjamin Herd Date: Wed, 22 Mar 2017 10:43:29 +0100 Subject: [PATCH 10/19] Added simple Java wrapper for double auction plus test classes --- pom.xml | 77 ++++++++++++++++ .../auctions/singleunit/JDoubleAuction.java | 89 +++++++++++++++++++ .../singleunit/JDoubleAuctionTest.java | 44 +++++++++ .../auctions/singleunit/OrderBookTest.java | 46 ++++++++++ .../auctions/singleunit/Service.java | 21 +++++ 5 files changed, 277 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java create mode 100644 src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java create mode 100644 src/main/java/org/economicsl/auctions/singleunit/OrderBookTest.java create mode 100644 src/main/java/org/economicsl/auctions/singleunit/Service.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7b16bff --- /dev/null +++ b/pom.xml @@ -0,0 +1,77 @@ + + 4.0.0 + org.economicsl + auctions + jar + 0.1.0-alpha + auctions + http://maven.apache.org + + 1.8 + 1.8 + UTF-8 + 2.12.1 + + + + + net.alchim31.maven + scala-maven-plugin + + + scala-compile + process-resources + + add-source + compile + + + + scala-test-compile + process-test-resources + + testCompile + + + + + + + + + + junit + junit + 4.12 + test + + + org.scala-lang + scala-library + 2.12.1 + + + net.alchim31.maven + scala-maven-plugin + 3.2.2 + + + diff --git a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java new file mode 100644 index 0000000..71c3299 --- /dev/null +++ b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java @@ -0,0 +1,89 @@ +// Copyright (c) 2017 Robert Bosch GmbH +// All rights reserved. +// +// 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 org.economicsl.auctions.singleunit; + +import org.economicsl.auctions.Tradable; +import org.economicsl.auctions.singleunit.pricing.PricingRule; +import scala.Option; +import scala.Tuple2; +import scala.collection.JavaConverters; +import scala.collection.immutable.Stream; + +import java.util.List; +import java.util.Optional; + +public class JDoubleAuction { + + public class ClearResult { + private JDoubleAuction auction; + private List> fills; + + public ClearResult(List> _fills, JDoubleAuction _auction) { + auction = _auction; + fills = _fills; + } + + public JDoubleAuction getAuction() { + return auction; + } + + public List> getFills() { + return fills; + } + } + + private DoubleAuction auction = DoubleAuction$.MODULE$.withEmptyOrderBook( + LimitAskOrder$.MODULE$.>ordering(), + LimitBidOrder$.MODULE$.>ordering().reverse() + ); + + private JDoubleAuction() { + } + + private JDoubleAuction(DoubleAuction _auction) { + auction = _auction; + } + + public JDoubleAuction insert(LimitAskOrder order) { + return new JDoubleAuction(auction.insert(order)); + } + + public JDoubleAuction insert(LimitBidOrder order) { + return new JDoubleAuction(auction.insert(order)); + } + + public JDoubleAuction remove(LimitAskOrder order) { + return new JDoubleAuction(auction.remove(order)); + } + + public JDoubleAuction remove(LimitBidOrder order) { + return new JDoubleAuction(auction.remove(order)); + } + + public Optional> clear(PricingRule p) { + Tuple2>>, DoubleAuction> clear = auction.clear(p); + Option>> streamOption = clear._1(); + List> fills = JavaConverters.seqAsJavaListConverter(clear._1().get()).asJava(); + JDoubleAuction newAuction = new JDoubleAuction(clear._2()); + return streamOption.isDefined() ? + Optional.of(new ClearResult(fills, newAuction)) : + Optional.empty(); + } + + public static JDoubleAuction withEmptyOrderBook() { + return new JDoubleAuction(); + } +} diff --git a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java new file mode 100644 index 0000000..bea1546 --- /dev/null +++ b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java @@ -0,0 +1,44 @@ +// Copyright (c) 2017 Robert Bosch GmbH +// All rights reserved. +// +// 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 org.economicsl.auctions.singleunit; + +import org.economicsl.auctions.singleunit.pricing.WeightedAveragePricingRule; +import scala.Option; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public class JDoubleAuctionTest { + + public static void main( String[] args ) { + JDoubleAuction auction = JDoubleAuction.withEmptyOrderBook(); + LimitBidOrder bid1 = LimitBidOrder$.MODULE$.apply(UUID.randomUUID(), 10.0, new Service()); + LimitAskOrder ask1 = LimitAskOrder$.MODULE$.apply(UUID.randomUUID(), 5.0, new Service()); + auction = auction.insert(bid1); + auction = auction.insert(ask1); + LimitBidOrder bid2 = LimitBidOrder$.MODULE$.apply(UUID.randomUUID(), 3.0, new Service()); + LimitAskOrder ask2 = LimitAskOrder$.MODULE$.apply(UUID.randomUUID(), 12.0, new Service()); + auction = auction.insert(bid2); + auction = auction.insert(ask2); + Optional.ClearResult> clear = auction.clear(new WeightedAveragePricingRule(1.0)); + if(clear.isPresent()) { + JDoubleAuction.ClearResult result = clear.get(); + auction = result.getAuction(); + result.getFills().forEach(fill -> System.out.println(fill)); + } + } +} diff --git a/src/main/java/org/economicsl/auctions/singleunit/OrderBookTest.java b/src/main/java/org/economicsl/auctions/singleunit/OrderBookTest.java new file mode 100644 index 0000000..8964bf5 --- /dev/null +++ b/src/main/java/org/economicsl/auctions/singleunit/OrderBookTest.java @@ -0,0 +1,46 @@ +// Copyright (c) 2017 Robert Bosch GmbH +// All rights reserved. +// +// 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 org.economicsl.auctions.singleunit; + +import org.economicsl.auctions.singleunit.orderbooks.FourHeapOrderBook; +import org.economicsl.auctions.singleunit.orderbooks.FourHeapOrderBook$; +import scala.Tuple2; +import scala.collection.JavaConverters; +import scala.collection.immutable.Stream; + +import java.util.List; +import java.util.UUID; + +public class OrderBookTest { + public static void main(String[] args) { + FourHeapOrderBook orderbook = FourHeapOrderBook$.MODULE$.empty( + LimitAskOrder$.MODULE$.>ordering(), + LimitBidOrder$.MODULE$.>ordering() + ); + + Service service = new Service(); + + LimitBidOrder bid1 = LimitBidOrder$.MODULE$.apply(UUID.randomUUID(), 5.0, service); + LimitAskOrder ask1 = LimitAskOrder$.MODULE$.apply(UUID.randomUUID(), 5.0, service); + + orderbook = orderbook.$plus(bid1); + orderbook = orderbook.$plus(ask1); + + Tuple2, LimitBidOrder>>, FourHeapOrderBook> tuple = orderbook.takeWhileMatched(); + List, LimitBidOrder>> matchedOrders = JavaConverters.seqAsJavaList(tuple._1()); + matchedOrders.forEach(t -> System.out.println(t)); + } +} diff --git a/src/main/java/org/economicsl/auctions/singleunit/Service.java b/src/main/java/org/economicsl/auctions/singleunit/Service.java new file mode 100644 index 0000000..16f4bb5 --- /dev/null +++ b/src/main/java/org/economicsl/auctions/singleunit/Service.java @@ -0,0 +1,21 @@ +// Copyright (c) 2017 Robert Bosch GmbH +// All rights reserved. +// +// 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 org.economicsl.auctions.singleunit; + +import org.economicsl.auctions.Tradable; + +public class Service implements Tradable { +} From dcbfd93e9f6535d921804d248a41668dc28b97c6 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 22 Mar 2017 16:05:34 +0300 Subject: [PATCH 11/19] Added compile ordering for Java. --- build.sbt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 5e50290..f9929b9 100644 --- a/build.sbt +++ b/build.sbt @@ -1,3 +1,5 @@ +import xsbti.compile + name := "auctions" version := "0.1.0-alpha" @@ -8,4 +10,8 @@ scalaVersion := "2.12.1" scalacOptions ++= Seq( "-feature", // tells the compiler to provide information about misused language features "-language:implicitConversions" // eliminates the need to import implicit conversions for each usage -) \ No newline at end of file +) + + +// In our project Java depends on Scala, but not the other way round! +compileOrder := CompileOrder.ScalaThenJava \ No newline at end of file From 18f57ddfe6b54a9a2b99fec24e1c7f67dc7c93a3 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 22 Mar 2017 16:18:22 +0300 Subject: [PATCH 12/19] Fixed compiler error? --- .../org/economicsl/auctions/singleunit/JDoubleAuction.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java index 71c3299..4a6a35a 100644 --- a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java +++ b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java @@ -15,6 +15,7 @@ package org.economicsl.auctions.singleunit; +import org.economicsl.auctions.Price; import org.economicsl.auctions.Tradable; import org.economicsl.auctions.singleunit.pricing.PricingRule; import scala.Option; @@ -73,7 +74,7 @@ public JDoubleAuction remove(LimitBidOrder order) { return new JDoubleAuction(auction.remove(order)); } - public Optional> clear(PricingRule p) { + public Optional> clear(PricingRule p) { Tuple2>>, DoubleAuction> clear = auction.clear(p); Option>> streamOption = clear._1(); List> fills = JavaConverters.seqAsJavaListConverter(clear._1().get()).asJava(); From 05259d3bfe823a2a429a2be703a7ea8877a04e49 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 22 Mar 2017 16:22:07 +0300 Subject: [PATCH 13/19] Pulled .travis.yml file from parent. --- .travis.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..10e5cde --- /dev/null +++ b/.travis.yml @@ -0,0 +1,32 @@ +dist: trusty + +sudo: false # builds will run on Docker infrastructure! + +language: scala + +scala: + - 2.10.6 + - 2.11.8 + - 2.12.1 + +jdk: + - oraclejdk8 + - openjdk8 + +branches: + only: + - master + - develop + +script: + - sbt clean compile + +before_cache: + # Tricks to avoid unnecessary cache updates + - find $HOME/.ivy2 -name "ivydata-*.properties" -delete + - find $HOME/.sbt -name "*.lock" -delete + +cache: + directories: + - $HOME/.ivy2/cache + - $HOME/.sbt/boot/ From 11c680e40c7ab4c5dd173fa01820000d160a3cff Mon Sep 17 00:00:00 2001 From: Benjamin Herd Date: Wed, 22 Mar 2017 14:31:07 +0100 Subject: [PATCH 14/19] Fixed bug in clear function (access to optional fills) --- .../auctions/singleunit/JDoubleAuction.java | 24 +++++++++++-------- .../singleunit/JDoubleAuctionTest.java | 1 - 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java index 4a6a35a..0b95e42 100644 --- a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java +++ b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java @@ -18,6 +18,9 @@ import org.economicsl.auctions.Price; import org.economicsl.auctions.Tradable; import org.economicsl.auctions.singleunit.pricing.PricingRule; +import org.economicsl.auctions.singleunit.pricing.WeightedAveragePricingRule; +import scala.AnyVal; +import scala.Double; import scala.Option; import scala.Tuple2; import scala.collection.JavaConverters; @@ -28,6 +31,11 @@ public class JDoubleAuction { + private DoubleAuction auction = DoubleAuction$.MODULE$.withEmptyOrderBook( + LimitAskOrder$.MODULE$.>ordering(), + LimitBidOrder$.MODULE$.>ordering().reverse() + ); + public class ClearResult { private JDoubleAuction auction; private List> fills; @@ -46,11 +54,6 @@ public List> getFills() { } } - private DoubleAuction auction = DoubleAuction$.MODULE$.withEmptyOrderBook( - LimitAskOrder$.MODULE$.>ordering(), - LimitBidOrder$.MODULE$.>ordering().reverse() - ); - private JDoubleAuction() { } @@ -77,11 +80,12 @@ public JDoubleAuction remove(LimitBidOrder order) { public Optional> clear(PricingRule p) { Tuple2>>, DoubleAuction> clear = auction.clear(p); Option>> streamOption = clear._1(); - List> fills = JavaConverters.seqAsJavaListConverter(clear._1().get()).asJava(); - JDoubleAuction newAuction = new JDoubleAuction(clear._2()); - return streamOption.isDefined() ? - Optional.of(new ClearResult(fills, newAuction)) : - Optional.empty(); + if(streamOption.isDefined()) { + List> fills = JavaConverters.seqAsJavaListConverter(clear._1().get()).asJava(); + JDoubleAuction newAuction = new JDoubleAuction(clear._2()); + return Optional.of(new ClearResult(fills, newAuction)); + } + return Optional.empty(); } public static JDoubleAuction withEmptyOrderBook() { diff --git a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java index bea1546..a8966fc 100644 --- a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java +++ b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java @@ -18,7 +18,6 @@ import org.economicsl.auctions.singleunit.pricing.WeightedAveragePricingRule; import scala.Option; -import java.util.List; import java.util.Optional; import java.util.UUID; From e533243c9d1b71b0cc91fb40b7badb66cee36d9e Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Wed, 22 Mar 2017 17:10:10 +0300 Subject: [PATCH 15/19] Removed unused imports. --- .../org/economicsl/auctions/singleunit/JDoubleAuctionTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java index bea1546..34ad106 100644 --- a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java +++ b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java @@ -16,9 +16,7 @@ package org.economicsl.auctions.singleunit; import org.economicsl.auctions.singleunit.pricing.WeightedAveragePricingRule; -import scala.Option; -import java.util.List; import java.util.Optional; import java.util.UUID; From d1228a160b4dd4366a68f046bffb3d274ee3fd12 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Thu, 23 Mar 2017 11:19:29 +0300 Subject: [PATCH 16/19] Refactored the DoubleAuction trait; added an implementation that allows for discriminatory pricing. --- .../scala/org/economicsl/auctions/sandbox.sc | 51 +++++---- .../auctions/singleunit/DoubleAuction.scala | 101 ++++++++++++++---- .../orderbooks/FourHeapOrderBook.scala | 10 +- .../singleunit/orderbooks/MatchedOrders.scala | 8 ++ 4 files changed, 127 insertions(+), 43 deletions(-) diff --git a/src/main/scala/org/economicsl/auctions/sandbox.sc b/src/main/scala/org/economicsl/auctions/sandbox.sc index e16e6c2..b8176cd 100644 --- a/src/main/scala/org/economicsl/auctions/sandbox.sc +++ b/src/main/scala/org/economicsl/auctions/sandbox.sc @@ -1,7 +1,7 @@ import java.util.UUID import org.economicsl.auctions._ -import org.economicsl.auctions.singleunit.DoubleAuction +import org.economicsl.auctions.singleunit.{DoubleAuction, Fill} import org.economicsl.auctions.singleunit.orderbooks.FourHeapOrderBook import org.economicsl.auctions.singleunit.pricing._ @@ -22,11 +22,9 @@ order1.value // Create a multi-unit market ask order... val order2: multiunit.MarketAskOrder[Google] = multiunit.MarketAskOrder(issuer, Quantity(100), google) -// Create a single-unit market ask order... -val order3: singleunit.MarketAskOrder[Google] = singleunit.MarketAskOrder(issuer, google) - -// Create a single-unit limit ask order... -val order4: singleunit.LimitAskOrder[Google] = singleunit.LimitAskOrder(issuer, Price(5.5), google) +// Create some single-unit limit ask orders... +val order3: singleunit.LimitAskOrder[Google] = singleunit.LimitAskOrder(issuer, Price(5.0), google) +val order4: singleunit.LimitAskOrder[Google] = singleunit.LimitAskOrder(issuer, Price(6.0), google) // Create a multi-unit limit bid order... val order5: multiunit.LimitBidOrder[Google] = multiunit.LimitBidOrder(issuer, Price(10), Quantity(100), google) @@ -34,11 +32,9 @@ val order5: multiunit.LimitBidOrder[Google] = multiunit.LimitBidOrder(issuer, Pr // Create a multi-unit market bid order... val order7: multiunit.MarketBidOrder[Google] = multiunit.MarketBidOrder(issuer, Quantity(100), google) -// Create a single-unit market bid order... -val order8: singleunit.MarketBidOrder[Google] = singleunit.MarketBidOrder(issuer, google) - -// Create a single-unit limit bid order... -val order9: singleunit.LimitBidOrder[Google] = singleunit.LimitBidOrder(issuer, Price(9.5), google) +// Create some single-unit limit bid orders... +val order8: singleunit.LimitBidOrder[Google] = singleunit.LimitBidOrder(issuer, Price(10.0), google) +val order9: singleunit.LimitBidOrder[Google] = singleunit.LimitBidOrder(issuer, Price(6.0), google) // Create an order for some other tradable val apple = new Apple() @@ -51,6 +47,9 @@ val orderBook3 = orderBook2 + order4 val orderBook4 = orderBook3 + order9 val orderBook5 = orderBook4 + order8 +val (matchedOrders, _) = orderBook5.takeAllMatched +matchedOrders.toList + // this should not compile...and it doesn't! // orderBook5 + order10 @@ -71,18 +70,32 @@ val averagePricing = new WeightedAveragePricingRule[Google](0.75) val averagePrice = averagePricing(orderBook5) // take a look at paired orders -val (pairedOrders, _) = orderBook5.takeWhileMatched +val (pairedOrders, _) = orderBook5.takeAllMatched pairedOrders.toList -// example usage of the auction... -val auction = DoubleAuction.withEmptyOrderBook[Google] -val auction2 = auction.insert(order4) -val auction3 = auction2.insert(order9) +// example usage of a double auction with uniform pricing... +val auction = DoubleAuction.withUniformPricing[Google] +val auction2 = auction.insert(order3) +val auction3 = auction2.insert(order4) +val auction4 = auction3.insert(order9) +val auction5 = auction4.insert(order8) // thanks to @bherd-rb we can do things like this... -val (result, _) = auction3.clear(midPointPricing) +val (result, _) = auction5.clear(midPointPricing) result.map(fills => fills.toList) // ...trivial to re-run the same auction with a different pricing rule! -val (result2, _) = auction3.clear(askQuotePricing) -result2.map(fills => fills.toList) \ No newline at end of file +val (result2, _) = auction5.clear(askQuotePricing) +result2.map(fills => fills.toList) + + +// example usage of a double auction with uniform pricing... +val auction6 = DoubleAuction.withDiscriminatoryPricing[Google] +val auction7 = auction6.insert(order3) +val auction8 = auction7.insert(order4) +val auction9 = auction8.insert(order9) +val auction10 = auction9.insert(order8) + +// thanks to @bherd-rb we can do things like this... +val (result3, _) = auction10.clear(midPointPricing) +result3.map(fills => fills.toList) diff --git a/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala b/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala index 8fff479..cde0685 100644 --- a/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala +++ b/src/main/scala/org/economicsl/auctions/singleunit/DoubleAuction.scala @@ -20,41 +20,96 @@ import org.economicsl.auctions.singleunit.orderbooks.FourHeapOrderBook import org.economicsl.auctions.singleunit.pricing.PricingRule -class DoubleAuction[T <: Tradable] private(orderBook: FourHeapOrderBook[T]) { +/** Base trait for all double auction implementations. */ +trait DoubleAuction[T <: Tradable] { - def insert(order: LimitAskOrder[T]): DoubleAuction[T] = { - new DoubleAuction(orderBook + order) - } + def insert(order: LimitAskOrder[T]): DoubleAuction[T] - def insert(order: LimitBidOrder[T]): DoubleAuction[T] = { - new DoubleAuction(orderBook + order) - } + def insert(order: LimitBidOrder[T]): DoubleAuction[T] + + def remove(order: LimitAskOrder[T]): DoubleAuction[T] + + def remove(order: LimitBidOrder[T]): DoubleAuction[T] + + def clear(p: PricingRule[T, Price]): (Option[Stream[Fill[T]]], DoubleAuction[T]) - def remove(order: LimitAskOrder[T]): DoubleAuction[T] = { - new DoubleAuction(orderBook - order) +} + + +object DoubleAuction { + + def withUniformPricing[T <: Tradable](implicit askOrdering: Ordering[LimitAskOrder[T]], bidOrdering: Ordering[LimitBidOrder[T]]): DoubleAuction[T] = { + new UniformPriceImpl[T](FourHeapOrderBook.empty[T](askOrdering, bidOrdering)) } - def remove(order: LimitBidOrder[T]): DoubleAuction[T] = { - new DoubleAuction(orderBook - order) + def withDiscriminatoryPricing[T <: Tradable](implicit askOrdering: Ordering[LimitAskOrder[T]], bidOrdering: Ordering[LimitBidOrder[T]]): DoubleAuction[T] = { + new DiscriminatoryPriceImpl[T](FourHeapOrderBook.empty[T](askOrdering, bidOrdering)) } - def clear(p: PricingRule[T, Price]): (Option[Stream[Fill[T]]], DoubleAuction[T]) = { - p(orderBook) match { - case Some(price) => - val (pairedOrders, newOrderBook) = orderBook.takeWhileMatched - val fills = pairedOrders.map { case (askOrder, bidOrder) => Fill(askOrder, bidOrder, price) } - (Some(fills), new DoubleAuction(newOrderBook)) - case None => (None, new DoubleAuction(orderBook)) + private[this] class UniformPriceImpl[T <: Tradable] (orderBook: FourHeapOrderBook[T]) extends DoubleAuction[T] { + + def insert(order: LimitAskOrder[T]): DoubleAuction[T] = { + new UniformPriceImpl(orderBook + order) } - } -} + def insert(order: LimitBidOrder[T]): DoubleAuction[T] = { + new UniformPriceImpl(orderBook + order) + } + def remove(order: LimitAskOrder[T]): DoubleAuction[T] = { + new UniformPriceImpl(orderBook - order) + } -object DoubleAuction { + def remove(order: LimitBidOrder[T]): DoubleAuction[T] = { + new UniformPriceImpl(orderBook - order) + } + + def clear(p: PricingRule[T, Price]): (Option[Stream[Fill[T]]], DoubleAuction[T]) = { + p(orderBook) match { + case Some(price) => + val (pairedOrders, newOrderBook) = orderBook.takeAllMatched + val fills = pairedOrders.map { case (askOrder, bidOrder) => Fill(askOrder, bidOrder, price) } + (Some(fills), new UniformPriceImpl(newOrderBook)) + case None => (None, new UniformPriceImpl(orderBook)) + } + } - def withEmptyOrderBook[T <: Tradable](implicit askOrdering: Ordering[LimitAskOrder[T]], bidOrdering: Ordering[LimitBidOrder[T]]): DoubleAuction[T] = { - new DoubleAuction(FourHeapOrderBook.empty[T](askOrdering, bidOrdering)) } + + private[this] class DiscriminatoryPriceImpl[T <: Tradable] (orderBook: FourHeapOrderBook[T]) extends DoubleAuction[T] { + + def insert(order: LimitAskOrder[T]): DoubleAuction[T] = { + new DiscriminatoryPriceImpl(orderBook + order) + } + + def insert(order: LimitBidOrder[T]): DoubleAuction[T] = { + new DiscriminatoryPriceImpl(orderBook + order) + } + + def remove(order: LimitAskOrder[T]): DoubleAuction[T] = { + new DiscriminatoryPriceImpl(orderBook - order) + } + + def remove(order: LimitBidOrder[T]): DoubleAuction[T] = { + new DiscriminatoryPriceImpl(orderBook - order) + } + + def clear(p: PricingRule[T, Price]): (Option[Stream[Fill[T]]], DoubleAuction[T]) = { + + @annotation.tailrec + def loop(fills: Stream[Fill[T]], ob: FourHeapOrderBook[T]): (Option[Stream[Fill[T]]], DoubleAuction[T]) = { + p(ob) match { + case None => (if (fills.nonEmpty) Some(fills) else None, new DiscriminatoryPriceImpl(ob)) + case Some(price) => + val (bestMatch, residual) = ob.takeBestMatched + val fill = bestMatch.map{ case (askOrder, bidOrder) => Fill(askOrder, bidOrder, price) } + loop(fill.fold(fills)(head => head #:: fills), residual) + } + } + loop(Stream.empty, orderBook) + + } + + } } diff --git a/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/FourHeapOrderBook.scala b/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/FourHeapOrderBook.scala index 133f9f0..bd4c91a 100644 --- a/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/FourHeapOrderBook.scala +++ b/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/FourHeapOrderBook.scala @@ -84,7 +84,15 @@ class FourHeapOrderBook[T <: Tradable] private(matchedOrders: MatchedOrders[T], case _ => None } - def takeWhileMatched: (Stream[(LimitAskOrder[T], LimitBidOrder[T])], FourHeapOrderBook[T]) = { + def takeBestMatched: (Option[(LimitAskOrder[T], LimitBidOrder[T])], FourHeapOrderBook[T]) = { + val (bestMatch, residualMatchedOrders) = matchedOrders.takeBestMatch + bestMatch match { + case result @ Some(_) => (result, new FourHeapOrderBook(residualMatchedOrders, unMatchedOrders)) + case None => (None, this) + } + } + + def takeAllMatched: (Stream[(LimitAskOrder[T], LimitBidOrder[T])], FourHeapOrderBook[T]) = { (matchedOrders.zipped, withEmptyMatchedOrders) } diff --git a/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/MatchedOrders.scala b/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/MatchedOrders.scala index 2067564..21589be 100644 --- a/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/MatchedOrders.scala +++ b/src/main/scala/org/economicsl/auctions/singleunit/orderbooks/MatchedOrders.scala @@ -48,6 +48,14 @@ class MatchedOrders[T <: Tradable] private(val askOrders: SortedAskOrders[T], va new MatchedOrders(askOrders, bidOrders - existing + incoming) } + def takeBestMatch: (Option[(LimitAskOrder[T], LimitBidOrder[T])], MatchedOrders[T]) = { + (askOrders.headOption, bidOrders.headOption) match { + case (Some(askOrder), Some(bidOrder)) => + (Some(askOrder, bidOrder), new MatchedOrders(askOrders - askOrder, bidOrders - bidOrder)) + case _ => (None, this) + } + } + def zipped: Stream[(LimitAskOrder[T], LimitBidOrder[T])] = { @annotation.tailrec def loop(askOrders: SortedAskOrders[T], bidOrders: SortedBidOrders[T], pairedOrders: Stream[(LimitAskOrder[T], LimitBidOrder[T])]): Stream[(LimitAskOrder[T], LimitBidOrder[T])] = { From 492953043486132f1d84b2a75cf97d4c37670f69 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Thu, 23 Mar 2017 11:20:06 +0300 Subject: [PATCH 17/19] Fixed complile error. --- .../org/economicsl/auctions/singleunit/JDoubleAuction.java | 5 +---- .../org/economicsl/auctions/singleunit/OrderBookTest.java | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java index 0b95e42..ebbd195 100644 --- a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java +++ b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java @@ -18,9 +18,6 @@ import org.economicsl.auctions.Price; import org.economicsl.auctions.Tradable; import org.economicsl.auctions.singleunit.pricing.PricingRule; -import org.economicsl.auctions.singleunit.pricing.WeightedAveragePricingRule; -import scala.AnyVal; -import scala.Double; import scala.Option; import scala.Tuple2; import scala.collection.JavaConverters; @@ -31,7 +28,7 @@ public class JDoubleAuction { - private DoubleAuction auction = DoubleAuction$.MODULE$.withEmptyOrderBook( + private DoubleAuction auction = DoubleAuction$.MODULE$.withUniformPricing( LimitAskOrder$.MODULE$.>ordering(), LimitBidOrder$.MODULE$.>ordering().reverse() ); diff --git a/src/main/java/org/economicsl/auctions/singleunit/OrderBookTest.java b/src/main/java/org/economicsl/auctions/singleunit/OrderBookTest.java index 8964bf5..a5b886d 100644 --- a/src/main/java/org/economicsl/auctions/singleunit/OrderBookTest.java +++ b/src/main/java/org/economicsl/auctions/singleunit/OrderBookTest.java @@ -39,7 +39,7 @@ public static void main(String[] args) { orderbook = orderbook.$plus(bid1); orderbook = orderbook.$plus(ask1); - Tuple2, LimitBidOrder>>, FourHeapOrderBook> tuple = orderbook.takeWhileMatched(); + Tuple2, LimitBidOrder>>, FourHeapOrderBook> tuple = orderbook.takeAllMatched(); List, LimitBidOrder>> matchedOrders = JavaConverters.seqAsJavaList(tuple._1()); matchedOrders.forEach(t -> System.out.println(t)); } From 84346771c373f0042491505021c4598d74aecdc9 Mon Sep 17 00:00:00 2001 From: davidrpugh Date: Thu, 23 Mar 2017 11:25:33 +0300 Subject: [PATCH 18/19] Made the difference between the pricing rules more obvious. --- src/main/scala/org/economicsl/auctions/sandbox.sc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/economicsl/auctions/sandbox.sc b/src/main/scala/org/economicsl/auctions/sandbox.sc index b8176cd..1c22660 100644 --- a/src/main/scala/org/economicsl/auctions/sandbox.sc +++ b/src/main/scala/org/economicsl/auctions/sandbox.sc @@ -1,7 +1,7 @@ import java.util.UUID import org.economicsl.auctions._ -import org.economicsl.auctions.singleunit.{DoubleAuction, Fill} +import org.economicsl.auctions.singleunit.DoubleAuction import org.economicsl.auctions.singleunit.orderbooks.FourHeapOrderBook import org.economicsl.auctions.singleunit.pricing._ @@ -82,14 +82,14 @@ val auction5 = auction4.insert(order8) // thanks to @bherd-rb we can do things like this... val (result, _) = auction5.clear(midPointPricing) -result.map(fills => fills.toList) +result.map(fills => fills.map(fill => fill.price).toList) // ...trivial to re-run the same auction with a different pricing rule! val (result2, _) = auction5.clear(askQuotePricing) -result2.map(fills => fills.toList) +result2.map(fills => fills.map(fill => fill.price).toList) -// example usage of a double auction with uniform pricing... +// example usage of a double auction with discriminatory pricing... val auction6 = DoubleAuction.withDiscriminatoryPricing[Google] val auction7 = auction6.insert(order3) val auction8 = auction7.insert(order4) @@ -98,4 +98,8 @@ val auction10 = auction9.insert(order8) // thanks to @bherd-rb we can do things like this... val (result3, _) = auction10.clear(midPointPricing) -result3.map(fills => fills.toList) +result3.map(fills => fills.map(fill => fill.price).toList) + +// ...trivial to re-run the same auction with a different pricing rule! +val (result4, _) = auction10.clear(bidQuotePricing) +result4.map(fills => fills.map(fill => fill.price).toList) From f05b98687aec099ab891ad2a67d9a1d46c7fa29c Mon Sep 17 00:00:00 2001 From: Benjamin Herd Date: Thu, 23 Mar 2017 10:54:30 +0100 Subject: [PATCH 19/19] Adapted Java auction wrapper to new version of DoubleAuction --- .../auctions/singleunit/JDoubleAuction.java | 13 +++++++++++-- .../auctions/singleunit/JDoubleAuctionTest.java | 5 ++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java index ebbd195..d024782 100644 --- a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java +++ b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuction.java @@ -22,6 +22,7 @@ import scala.Tuple2; import scala.collection.JavaConverters; import scala.collection.immutable.Stream; +import scala.math.Ordering; import java.util.List; import java.util.Optional; @@ -85,7 +86,15 @@ public Optional> clear(PricingRule p) { return Optional.empty(); } - public static JDoubleAuction withEmptyOrderBook() { - return new JDoubleAuction(); + public static JDoubleAuction withUniformPricing( + Ordering> askOrdering, + Ordering> bidOrdering) { + return new JDoubleAuction(DoubleAuction$.MODULE$.withUniformPricing(askOrdering, bidOrdering)); + } + + public static JDoubleAuction withDiscriminatoryPricing( + Ordering> askOrdering, + Ordering> bidOrdering) { + return new JDoubleAuction(DoubleAuction$.MODULE$.withDiscriminatoryPricing(askOrdering, bidOrdering)); } } diff --git a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java index 34ad106..58baf89 100644 --- a/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java +++ b/src/main/java/org/economicsl/auctions/singleunit/JDoubleAuctionTest.java @@ -23,7 +23,10 @@ public class JDoubleAuctionTest { public static void main( String[] args ) { - JDoubleAuction auction = JDoubleAuction.withEmptyOrderBook(); + JDoubleAuction auction = JDoubleAuction.withUniformPricing( + LimitAskOrder$.MODULE$.>ordering(), + LimitBidOrder$.MODULE$.>ordering().reverse()); + LimitBidOrder bid1 = LimitBidOrder$.MODULE$.apply(UUID.randomUUID(), 10.0, new Service()); LimitAskOrder ask1 = LimitAskOrder$.MODULE$.apply(UUID.randomUUID(), 5.0, new Service()); auction = auction.insert(bid1);