Added a RoundToLotSizeElement element to marketmaker chain.
This commit is contained in:
parent
befb86bcfb
commit
b57ef58d63
|
@ -13,7 +13,6 @@
|
|||
# [Github](https://github.com/blockworks-foundation)
|
||||
# [Email](mailto:hello@blockworks.foundation)
|
||||
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from .token import Token
|
||||
|
@ -46,6 +45,18 @@ class LotSizeConverter():
|
|||
def quantity_lots_to_value(self, quantity_lots: Decimal) -> Decimal:
|
||||
return (quantity_lots * self.base_lot_size) / (10 ** self.base.decimals)
|
||||
|
||||
def round_base(self, quantity: Decimal) -> Decimal:
|
||||
base_factor: Decimal = 10 ** self.base.decimals
|
||||
rounded: int = round(quantity * base_factor)
|
||||
return Decimal(rounded) / base_factor
|
||||
|
||||
def round_quote(self, price: Decimal) -> Decimal:
|
||||
quote_factor: Decimal = 10 ** self.base.decimals
|
||||
base_factor: Decimal = 10 ** self.base.decimals
|
||||
lots: Decimal = (price * quote_factor * self.base_lot_size) / (base_factor * self.quote_lot_size)
|
||||
rounded: int = round(lots)
|
||||
return Decimal(rounded) / self.quote_lot_size
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"« 𝙻𝚘𝚝𝚂𝚒𝚣𝚎𝙲𝚘𝚗𝚟𝚎𝚛𝚝𝚎𝚛 [base lot size: {self.base_lot_size}, quote lot size: {self.quote_lot_size}] »"
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ from .confidenceintervalspreadelement import ConfidenceIntervalSpreadElement
|
|||
from .element import Element
|
||||
from .minimumchargeelement import MinimumChargeElement
|
||||
from .preventpostonlycrossingbookelement import PreventPostOnlyCrossingBookElement
|
||||
from .roundtolotsizeelement import RoundToLotSizeElement
|
||||
|
||||
|
||||
# # 🥭 ChainBuilder class
|
||||
|
@ -61,7 +62,8 @@ class ChainBuilder:
|
|||
ConfidenceIntervalSpreadElement(args.position_size_ratio, confidence_interval_levels, args.order_type),
|
||||
BiasQuoteOnPositionElement(args.quote_position_bias),
|
||||
MinimumChargeElement(args.minimum_charge_ratio),
|
||||
PreventPostOnlyCrossingBookElement()
|
||||
PreventPostOnlyCrossingBookElement(),
|
||||
RoundToLotSizeElement()
|
||||
]
|
||||
|
||||
return Chain(elements)
|
||||
|
|
|
@ -40,16 +40,16 @@ class PreventPostOnlyCrossingBookElement(Element):
|
|||
new_buy: mango.Order = order.with_price(new_buy_price)
|
||||
self.logger.debug(
|
||||
f"""Order change - would cross the orderbook {top_bid.price if top_bid else None} / {top_ask.price}:
|
||||
Old: {order}
|
||||
New: {new_buy}""")
|
||||
Old: {order}
|
||||
New: {new_buy}""")
|
||||
new_orders += [new_buy]
|
||||
elif order.side == mango.Side.SELL and top_bid is not None and top_ask is not None and order.price <= top_bid.price:
|
||||
new_sell_price: Decimal = top_ask.price + model_state.market.lot_size_converter.tick_size
|
||||
new_sell: mango.Order = order.with_price(new_sell_price)
|
||||
self.logger.debug(
|
||||
f"""Order change - would cross the orderbook {top_bid.price} / {top_ask.price if top_ask else None}:
|
||||
Old: {order}
|
||||
New: {new_sell}""")
|
||||
Old: {order}
|
||||
New: {new_sell}""")
|
||||
|
||||
new_orders += [new_sell]
|
||||
else:
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
# # ⚠ 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 .element import Element
|
||||
from ..modelstate import ModelState
|
||||
|
||||
|
||||
# # 🥭 RoundToLotSizeElement class
|
||||
#
|
||||
# May modifiy an `Order`s price or quantity to ensure it's exactly aligned to the market's lot sizes.
|
||||
#
|
||||
class RoundToLotSizeElement(Element):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def process(self, context: mango.Context, model_state: ModelState, orders: typing.Sequence[mango.Order]) -> typing.Sequence[mango.Order]:
|
||||
new_orders: typing.List[mango.Order] = []
|
||||
for order in orders:
|
||||
new_price: Decimal = model_state.market.lot_size_converter.round_quote(order.price)
|
||||
new_quantity: Decimal = model_state.market.lot_size_converter.round_base(order.quantity)
|
||||
new_order: mango.Order = order
|
||||
if (new_order.price != new_price) or (new_order.quantity != new_quantity):
|
||||
new_order = new_order.with_price(new_price).with_quantity(new_quantity)
|
||||
self.logger.debug(f"""Order change - price is now aligned to lot size:
|
||||
Old: {order}
|
||||
New: {new_order}""")
|
||||
|
||||
new_orders += [new_order]
|
||||
|
||||
return new_orders
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "« 𝚁𝚘𝚞𝚗𝚍𝚃𝚘𝙻𝚘𝚝𝚂𝚒𝚣𝚎𝙴𝚕𝚎𝚖𝚎𝚗𝚝 »"
|
|
@ -121,6 +121,11 @@ class Order(typing.NamedTuple):
|
|||
return Order(id=self.id, side=self.side, price=price, quantity=self.quantity,
|
||||
client_id=self.client_id, owner=self.owner, order_type=self.order_type)
|
||||
|
||||
# Returns an identical order with the quantity changed.
|
||||
def with_quantity(self, quantity: Decimal) -> "Order":
|
||||
return Order(id=self.id, side=self.side, price=self.price, quantity=quantity,
|
||||
client_id=self.client_id, owner=self.owner, order_type=self.order_type)
|
||||
|
||||
@staticmethod
|
||||
def from_serum_order(serum_order: PySerumOrder) -> "Order":
|
||||
price = Decimal(serum_order.info.price)
|
||||
|
|
Loading…
Reference in New Issue