mango-explorer/mango/marketmaking/toleranceorderreconciler.py

101 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# # ⚠ Warning
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# [🥭 Mango Markets](https://mango.markets/) support is available at:
# [Docs](https://docs.mango.markets/)
# [Discord](https://discord.gg/67jySBhxrg)
# [Twitter](https://twitter.com/mangomarkets)
# [Github](https://github.com/blockworks-foundation)
# [Email](mailto:hello@blockworks.foundation)
import mango
import typing
from decimal import Decimal
from .modelstate import ModelState
from .orderreconciler import OrderReconciler
from .reconciledorders import ReconciledOrders
# # 🥭 ToleranceOrderReconciler class
#
# Has a level of 'tolerance' around whether a desired order matches an existing order.
#
# There are two tolerance levels:
# * A tolerance for price matching
# * A tolderance for quantity matching
#
# Tolerances are expressed as a ratio. To match the existing value must be within +/- the tolderance
# of the desired value.
#
# Note:
# * A BUY only matches with a BUY, a SELL only matches with a SELL.
# * ID and Client ID are ignored when matching.
# * ModelState is ignored when matching.
#
class ToleranceOrderReconciler(OrderReconciler):
def __init__(self, price_tolerance: Decimal, quantity_tolerance: Decimal):
super().__init__()
self.price_tolerance: Decimal = price_tolerance
self.quantity_tolerance: Decimal = quantity_tolerance
def reconcile(self, _: ModelState, existing_orders: typing.Sequence[mango.Order], desired_orders: typing.Sequence[mango.Order]) -> ReconciledOrders:
remaining_existing_orders: typing.List[mango.Order] = list(existing_orders)
outcomes: ReconciledOrders = ReconciledOrders()
for desired in desired_orders:
acceptable = self.find_acceptable_order(desired, remaining_existing_orders)
if acceptable is None:
outcomes.to_place += [desired]
else:
outcomes.to_keep += [acceptable]
outcomes.to_ignore += [desired]
remaining_existing_orders.remove(acceptable)
# By this point we have removed all acceptable existing orders, so those that remain
# should be cancelled.
outcomes.to_cancel = remaining_existing_orders
in_count = len(existing_orders) + len(desired_orders)
out_count = len(outcomes.to_place) + len(outcomes.to_cancel) + len(outcomes.to_keep) + len(outcomes.to_ignore)
if in_count != out_count:
raise Exception(
f"Failure processing all desired orders. Count of orders in: {in_count}. Count of orders out: {out_count}.")
return outcomes
def find_acceptable_order(self, desired: mango.Order, existing_orders: typing.Sequence[mango.Order]) -> typing.Optional[mango.Order]:
for existing in existing_orders:
if self.is_within_tolderance(existing, desired):
return existing
return None
def is_within_tolderance(self, existing: mango.Order, desired: mango.Order) -> bool:
if existing.side != desired.side:
return False
price_tolerance: Decimal = existing.price * self.price_tolerance
if desired.price > (existing.price + price_tolerance):
return False
if desired.price < (existing.price - price_tolerance):
return False
quantity_tolerance: Decimal = existing.quantity * self.quantity_tolerance
if desired.quantity > (existing.quantity + quantity_tolerance):
return False
if desired.quantity < (existing.quantity - quantity_tolerance):
return False
return True
def __str__(self) -> str:
return f"« 𝚃𝚘𝚕𝚎𝚛𝚊𝚗𝚌𝚎𝙾𝚛𝚍𝚎𝚛𝚁𝚎𝚌𝚘𝚗𝚌𝚒𝚕𝚎𝚛 [price tolerance: {self.price_tolerance}, quantity tolerance: {self.quantity_tolerance}] »"