Added --hedging-target-balance parameter to marketmaker.
This commit is contained in:
parent
62b8ce8c49
commit
69f2fd49d6
|
@ -8,6 +8,7 @@ import rx
|
|||
import rx.operators
|
||||
import sys
|
||||
import threading
|
||||
import typing
|
||||
|
||||
from decimal import Decimal
|
||||
from solana.publickey import PublicKey
|
||||
|
@ -43,6 +44,8 @@ parser.add_argument("--hedging-max-price-slippage-factor", type=Decimal, default
|
|||
help="the maximum value the IOC hedging order price can slip by when hedging (default is 0.05 for 5%%)")
|
||||
parser.add_argument("--hedging-max-chunk-quantity", type=Decimal, default=Decimal(0),
|
||||
help="the maximum quantity of the hedge asset that will be traded in a single pulse. Trades larger than this size will be 'chunked' and spread across subsequent hedge pulses.")
|
||||
parser.add_argument("--hedging-target-balance", type=mango.parse_fixed_target_balance, required=False,
|
||||
help="hedged balance to maintain - format is a token symbol plus target value, separated by a colon (e.g. 'ETH:2.5')")
|
||||
parser.add_argument("--account-address", type=PublicKey,
|
||||
help="address of the specific account to use, if more than one available")
|
||||
parser.add_argument("--notify-errors", type=mango.parse_subscription_target, action="append", default=[],
|
||||
|
@ -118,9 +121,12 @@ if args.hedging_market is not None:
|
|||
hedging_market_operations: mango.MarketOperations = mango.create_market_operations(
|
||||
context, wallet, account, hedging_market, args.dry_run)
|
||||
|
||||
target_balance: typing.Optional[mango.TargetBalance] = args.hedging_target_balance
|
||||
if target_balance is None:
|
||||
target_balance = mango.FixedTargetBalance(hedging_market.base.symbol, Decimal(0))
|
||||
hedger = mango.hedging.PerpToSpotHedger(group, underlying_market, hedging_market,
|
||||
hedging_market_operations, args.hedging_max_price_slippage_factor,
|
||||
args.hedging_max_chunk_quantity)
|
||||
args.hedging_max_chunk_quantity, target_balance)
|
||||
|
||||
|
||||
order_reconciler = mango.marketmaking.ToleranceOrderReconciler(
|
||||
|
|
|
@ -82,4 +82,19 @@ This parameter allows you to specify a maximum quantity to trade on a single 'pu
|
|||
|
||||
For example, if the marketmaker wants to hedge 50 SOL, it might be better to spread that out over 5 pulses with 10 SOL each. That gives the spot marketmakers a chance to adjust and put up fresh orders instead of just sweeping all the orders on the book.
|
||||
|
||||
Using this may give a better overall price than a single order, or it may introduce a new timing risk as the market moves further away while some of the position is unhedged.
|
||||
Using this may give a better overall price than a single order, or it may introduce a new timing risk as the market moves further away while some of the position is unhedged.
|
||||
|
||||
|
||||
> Parameter: `--hedging-target-balance`
|
||||
|
||||
> Example Usage: `--hedging-target-balance BTC:2.5`
|
||||
|
||||
Sometimes when hedging, it's preferable to have a specific balance to hedge from rather than purely zero.
|
||||
|
||||
For example, you might want to add 2.5 BTC to your Mango account, earn interest on it, and use it for collateral for your marketmaking. You wouln't want the hedging functionality to immediately sell it to aim for a neutral position.
|
||||
|
||||
Instead you'd want your 2.4 BTC to be your neutral position. If you sold 0.5 BTC-PERP short, instead of the hedger aiming for 0.5 BTC you'd want it to take your 2.5 BTC into account and aim for 3 BTC.
|
||||
|
||||
The `--hedging-target-balance` parameter allows you to specify what the target (neutral) position should be. The hedging functionality will then aim to achieve that target, with perp positions and spot balances adding and subtracting from it instead of 0.
|
||||
|
||||
The format for the parameter is the same format as target balances for the `balance-account` and `balance-wallet` commands: it's the symbol, followed by a colon, followed by a fixed number. Percentage target balances aren't supported for this parameter, and the symbol must match the symbol of the base token on the `--hedging-market`.
|
|
@ -50,7 +50,6 @@ from .oraclefactory import create_oracle_provider
|
|||
from .parse_account_info_to_orders import parse_account_info_to_orders
|
||||
from .perpaccount import PerpAccount
|
||||
from .perpeventqueue import PerpEvent, PerpFillEvent, PerpOutEvent, PerpUnknownEvent, PerpEventQueue, UnseenPerpEventChangesTracker
|
||||
from .perphedger import PerpHedger
|
||||
from .perpmarket import PerpMarket, PerpMarketStub
|
||||
from .perpmarketdetails import PerpMarketDetails
|
||||
from .perpmarketinfo import PerpMarketInfo
|
||||
|
|
|
@ -31,18 +31,26 @@ from .hedger import Hedger
|
|||
class PerpToSpotHedger(Hedger):
|
||||
def __init__(self, group: mango.Group, underlying_market: mango.PerpMarket,
|
||||
hedging_market: mango.SpotMarket, market_operations: mango.MarketOperations,
|
||||
max_price_slippage_factor: Decimal, max_hedge_chunk_quantity: Decimal):
|
||||
max_price_slippage_factor: Decimal, max_hedge_chunk_quantity: Decimal,
|
||||
target_balance: mango.TargetBalance):
|
||||
super().__init__()
|
||||
if (underlying_market.base != hedging_market.base) or (underlying_market.quote != hedging_market.quote):
|
||||
raise Exception(
|
||||
f"Market {hedging_market.symbol} cannot be used to hedge market {underlying_market.symbol}.")
|
||||
|
||||
if target_balance.symbol != hedging_market.base.symbol:
|
||||
raise Exception(f"Cannot target {target_balance.symbol} when hedging on {hedging_market.symbol}")
|
||||
|
||||
self.underlying_market: mango.PerpMarket = underlying_market
|
||||
self.hedging_market: mango.SpotMarket = hedging_market
|
||||
self.market_operations: mango.MarketOperations = market_operations
|
||||
self.buy_price_adjustment_factor: Decimal = Decimal("1") + max_price_slippage_factor
|
||||
self.sell_price_adjustment_factor: Decimal = Decimal("1") - max_price_slippage_factor
|
||||
self.max_hedge_chunk_quantity: Decimal = max_hedge_chunk_quantity
|
||||
|
||||
resolved_target: mango.TokenValue = target_balance.resolve(hedging_market.base, Decimal(0), Decimal(0))
|
||||
self.target_balance: Decimal = self.hedging_market.lot_size_converter.round_base(resolved_target.value)
|
||||
|
||||
self.market_index: int = group.find_perp_market_index(underlying_market.address)
|
||||
|
||||
def pulse(self, context: mango.Context, model_state: mango.ModelState):
|
||||
|
@ -66,7 +74,8 @@ class PerpToSpotHedger(Hedger):
|
|||
token_balance_rounded: Decimal = self.hedging_market.lot_size_converter.round_base(token_balance.value)
|
||||
|
||||
# When we add the rounded perp position and token balances, we should get zero if we're delta-neutral.
|
||||
delta: Decimal = perp_position_rounded + token_balance_rounded
|
||||
# If we have a target balance, subtract that to get our targetted delta neutral balance.
|
||||
delta: Decimal = perp_position_rounded + token_balance_rounded - self.target_balance
|
||||
self.logger.debug(
|
||||
f"Delta from {self.underlying_market.symbol} to {self.hedging_market.symbol} is {delta:,.8f} {basket_token.token_info.token.symbol}")
|
||||
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
# # ⚠ 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 logging
|
||||
import traceback
|
||||
|
||||
from decimal import Decimal
|
||||
from solana.publickey import PublicKey
|
||||
|
||||
from .context import Context
|
||||
from .orders import Side, OrderType, Order
|
||||
from .marketoperations import MarketOperations
|
||||
from .perpeventqueue import PerpFillEvent
|
||||
|
||||
|
||||
# # 🥭 PerpHedger class
|
||||
#
|
||||
# A `PerpHedger` takes a `PerpFill` event and tries to hedge (apply the reverse trade) on the spot market.
|
||||
#
|
||||
class PerpHedger:
|
||||
def __init__(self, own_address: PublicKey, hedge_market_operations: MarketOperations, max_price_slippage_factor: Decimal):
|
||||
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
||||
self.own_address: PublicKey = own_address
|
||||
self.hedge_market_operations: MarketOperations = hedge_market_operations
|
||||
self.buy_price_adjustment_factor: Decimal = Decimal("1") + max_price_slippage_factor
|
||||
self.sell_price_adjustment_factor: Decimal = Decimal("1") - max_price_slippage_factor
|
||||
self.hedging_on: str = self.hedge_market_operations.market.symbol
|
||||
|
||||
def hedge(self, context: Context, fill_event: PerpFillEvent) -> Order:
|
||||
side: Side
|
||||
if fill_event.taker == self.own_address:
|
||||
opposite_side: Side = Side.BUY if fill_event.taker_side == Side.SELL else Side.SELL
|
||||
side = opposite_side
|
||||
else:
|
||||
# We were the opposite side in the filled trade, so to hedge we want to be the side the taker just took.
|
||||
side = fill_event.taker_side
|
||||
up_or_down: str = "up to" if side == Side.BUY else "down to"
|
||||
|
||||
price_adjustment_factor: Decimal = self.sell_price_adjustment_factor if side == Side.SELL else self.buy_price_adjustment_factor
|
||||
adjusted_price: Decimal = fill_event.price * price_adjustment_factor
|
||||
quantity: Decimal = fill_event.quantity
|
||||
order: Order = Order.from_basic_info(side, adjusted_price, fill_event.quantity, OrderType.IOC)
|
||||
self.logger.info(
|
||||
f"Hedging perp {fill_event.taker_side} ({fill_event.maker_order_id}) of {fill_event.quantity:,.8f} at {fill_event.price:,.8f} with {side} of {quantity:,.8f} at {up_or_down} {adjusted_price:,.8f} on {self.hedging_on}\n\t{order}")
|
||||
try:
|
||||
self.hedge_market_operations.place_order(order)
|
||||
except Exception:
|
||||
self.logger.error(
|
||||
f"[{context.name}] Failed to hedge on {self.hedging_on} using order {order} - {traceback.format_exc()}")
|
||||
return order
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"""« 𝙿𝚎𝚛𝚙𝙷𝚎𝚍𝚐𝚎𝚛 hedging on '{self.hedging_on}' [BUY * {self.buy_price_adjustment_factor} / SELL * {self.sell_price_adjustment_factor}] »"""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self}"
|
Loading…
Reference in New Issue