ScalABM/auctions

Should an issuer only be able to have one active order in the order book?

Opened this issue · 4 comments

@bherd-rb In both of the Wellman et al papers, it is assumed that a market participant can only have one active ask or bid order in the order book at a time. If a market participant submits a new order, it simply replaces the existing order.

Can you think of a use-case for you in which this would be problematic?

@davidrpugh That's a very good question. I would say that, if we can guarantee that all the information necessary for matching buyers and sellers is covered by the type of the tradable, then we're fine. But are we going to run into a granularity problem? For example, what happens if we want sensor data to be traded. The actual nature of the sensor data is characterised by a variety of attributes (e.g. sensor type, time, quality, ...). Are we going to put all of this information into the type? That's probably not going to work. Or are we going to have a tradable SensorData and the details of the data are hidden in the member variables of the instances stored in the book? Because then, strictly speaking, we have order books that store different types of tradable. In that case, an agent might definitely want to place multiple bids and asks for the same type but with different attributes.

Does that make sense?! I was just thinking out loud. Maybe I'm still misinterpreting the semantics of bids, asks, and order books.

@bherd-rb Good point. For the kind of thing you are talking about we will definitely need a more general order book than the FourHeapOrderBook and a more general way of think about preferences than we currently have in the API.

I might have just what we need in Agora. I have an OrderBook trait similar to the following...

trait OrderBook[+O <: Order[_ <: Tradable], OB <: OrderBook[O, OB] {

  /** Add an order...*/ 
  def + (kv: (UUID, O): OB 

  /** Remove an order...*/
  def - (kv: (UUID, O): OB

  /** Filter the `OrderBook` and return those `Order` instances satisfying the given predicate.
    *
    * @param p predicate defining desirable `Order` characteristics.
    * @return collection of `Order` instances satisfying the given predicate.
    */
  def filter(p: (O) => Boolean): Option[GenIterable[O]]

  /** Find the first `Order` in the `OrderBook` that satisfies the given predicate.
    *
    * @param p predicate defining desirable `Order` characteristics.
    * @return `None` if no `Order` in the `OrderBook` satisfies the predicate; `Some(order)` otherwise.
    */
  def find(p: (O) => Boolean): Option[O]

  /** Return the head `Order` of the `OrderBook`.
    *
    * @return `None` if the `OrderBook` is empty; `Some(order)` otherwise.
    */
  def headOption: Option[O]

  /** Boolean flag indicating whether or not the `OrderBook` contains `Order` instances.
    *
    * @return `true`, if the `OrderBook` does not contain any `Order` instances; `false`, otherwise.
    */
  def isEmpty: Boolean

  /** Boolean flag indicating whether or not the `OrderBook` contains `Order` instances.
    *
    * @return `true`, if the `OrderBook` contains any `Order` instances; `false`, otherwise.
    */
  def nonEmpty: Boolean

  /** Applies a binary operator to a start value and all existing orders of the `OrderBook`, going left to right.
    *
    * @tparam P the return type of the binary operator
    */
  def foldLeft[P](z: P)(op: (P, O) => P): P

  /** Reduces the existing orders of this `OrderBook`, if any, using the specified associative binary operator.
    *
    * @param op an associative binary operator.
    * @return `None` if the `OrderBook` is empty; the result of applying the `op` to the existing orders in the `OrderBook` otherwise.
    * @note reducing the existing orders of an `OrderBook` is an `O(n)` operation. The order in which operations are  performed on elements is unspecified and may be nondeterministic depending on the type of `OrderBook`.
    */
  def reduceOption[T >: O](op: (T, T) => T): Option[T]

  /** Backing store for the existing orders. */
  protected def existingOrders: GenIterable[(UUID, O)]

}

We would also need a more flexible way to express preferences over tradables of a particular type than we have at present. For example, we could have the following trait...

trait Predicate[-T <: Tradable] {

  /** Boolean function used to determine whether some `T` is acceptable.
    *
    * @return a boolean function that returns `true` if the instance of `T` is acceptable; `false` otherwise.
    */
  def isAcceptable: (T) => Boolean

}

Now suppose that we had some tradable sensor data...

```scala
type Sensor = Any
type Timestamp = Long
case class SensorData(sensor: Sensor, quantity: Quantity, timestamp: Timestamp) extends Tradable

...then we could create an order for the sensor data that looks something like...

case class SensorOrder(p: (SensorData) => Boolean) extends Order[SensorData] with Predicate[SensorData] {
  val isAcceptable: (SensorData) => Boolean = p
}

...idea would be to then define and OrderBook for SensorOrder that would allow us to use the predicate function on the SensorOrder together with the methods on the OrderBook to match the order with some other order that satisfies the predicate function.

Thoughts? I will open a PR to pull in the relevant components from Agora to implement this.

@davidrpugh I think that solution is really good! It would be helpful (for me) to evaluate it in a concrete scenario. I could, for example, try to extend the booking of transportation services in my current mobility example with additional attributes (e.g. trip length, car type, etc.). That might be a good use case for the solution above.

@bherd-rb A non-financial concrete use case would be great! I am pretty sure that the above idea will work for most financial market models, but have not spent enough time thinking about non-financial market contexts.