2021-06-25 02:33:40 -07:00
|
|
|
|
# # ⚠ 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)
|
|
|
|
|
|
2021-09-06 09:07:26 -07:00
|
|
|
|
import rx
|
|
|
|
|
import rx.disposable
|
|
|
|
|
import rx.subject
|
|
|
|
|
import rx.operators as ops
|
2021-07-23 06:18:26 -07:00
|
|
|
|
import typing
|
|
|
|
|
|
2021-06-25 02:33:40 -07:00
|
|
|
|
from solana.publickey import PublicKey
|
|
|
|
|
|
|
|
|
|
from .accountinfo import AccountInfo
|
|
|
|
|
from .context import Context
|
2021-06-25 07:50:37 -07:00
|
|
|
|
from .group import Group
|
2021-09-06 09:07:26 -07:00
|
|
|
|
from .loadedmarket import LoadedMarket
|
2021-08-22 11:48:20 -07:00
|
|
|
|
from .lotsizeconverter import LotSizeConverter, RaisingLotSizeConverter
|
2021-07-23 06:18:26 -07:00
|
|
|
|
from .market import Market, InventorySource
|
2021-09-06 09:07:26 -07:00
|
|
|
|
from .observables import DisposingSubject, observable_pipeline_error_reporter
|
2021-08-21 14:06:58 -07:00
|
|
|
|
from .orderbookside import PerpOrderBookSide
|
2021-07-23 06:18:26 -07:00
|
|
|
|
from .orders import Order
|
2021-09-06 09:07:26 -07:00
|
|
|
|
from .perpeventqueue import PerpEvent, PerpEventQueue, UnseenPerpEventChangesTracker
|
2021-07-23 06:18:26 -07:00
|
|
|
|
from .perpmarketdetails import PerpMarketDetails
|
2021-11-08 03:39:09 -08:00
|
|
|
|
from .token import Instrument, Token
|
2021-07-23 06:18:26 -07:00
|
|
|
|
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
# # 🥭 PerpMarket class
|
|
|
|
|
#
|
2021-07-23 06:18:26 -07:00
|
|
|
|
# This class encapsulates our knowledge of a Mango perps market.
|
|
|
|
|
#
|
2021-09-06 09:07:26 -07:00
|
|
|
|
class PerpMarket(LoadedMarket):
|
2021-11-08 03:39:09 -08:00
|
|
|
|
def __init__(self, mango_program_address: PublicKey, address: PublicKey, base: Instrument, quote: Token, underlying_perp_market: PerpMarketDetails):
|
2021-08-26 02:31:02 -07:00
|
|
|
|
super().__init__(mango_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
2021-07-23 06:18:26 -07:00
|
|
|
|
self.underlying_perp_market: PerpMarketDetails = underlying_perp_market
|
2021-07-28 09:43:58 -07:00
|
|
|
|
self.lot_size_converter: LotSizeConverter = LotSizeConverter(
|
|
|
|
|
base, underlying_perp_market.base_lot_size, quote, underlying_perp_market.quote_lot_size)
|
2021-07-23 06:18:26 -07:00
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def symbol(self) -> str:
|
|
|
|
|
return f"{self.base.symbol}-PERP"
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def group(self) -> Group:
|
|
|
|
|
return self.underlying_perp_market.group
|
|
|
|
|
|
2021-10-26 10:45:04 -07:00
|
|
|
|
@property
|
|
|
|
|
def bids_address(self) -> PublicKey:
|
|
|
|
|
return self.underlying_perp_market.bids
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def asks_address(self) -> PublicKey:
|
|
|
|
|
return self.underlying_perp_market.asks
|
|
|
|
|
|
|
|
|
|
def parse_account_info_to_orders(self, account_info: AccountInfo) -> typing.Sequence[Order]:
|
|
|
|
|
side: PerpOrderBookSide = PerpOrderBookSide.parse(account_info, self.underlying_perp_market)
|
|
|
|
|
return side.orders()
|
|
|
|
|
|
2021-07-23 06:18:26 -07:00
|
|
|
|
def unprocessed_events(self, context: Context) -> typing.Sequence[PerpEvent]:
|
2021-07-28 09:43:58 -07:00
|
|
|
|
event_queue: PerpEventQueue = PerpEventQueue.load(
|
|
|
|
|
context, self.underlying_perp_market.event_queue, self.lot_size_converter)
|
2021-08-01 09:47:45 -07:00
|
|
|
|
return event_queue.unprocessed_events
|
2021-07-23 06:18:26 -07:00
|
|
|
|
|
|
|
|
|
def accounts_to_crank(self, context: Context, additional_account_to_crank: typing.Optional[PublicKey]) -> typing.Sequence[PublicKey]:
|
|
|
|
|
accounts_to_crank: typing.List[PublicKey] = []
|
|
|
|
|
for event_to_crank in self.unprocessed_events(context):
|
|
|
|
|
accounts_to_crank += event_to_crank.accounts_to_crank
|
|
|
|
|
|
|
|
|
|
if additional_account_to_crank is not None:
|
|
|
|
|
accounts_to_crank += [additional_account_to_crank]
|
|
|
|
|
|
|
|
|
|
seen = []
|
|
|
|
|
distinct = []
|
|
|
|
|
for account in accounts_to_crank:
|
|
|
|
|
account_str = account.to_base58()
|
|
|
|
|
if account_str not in seen:
|
|
|
|
|
distinct += [account]
|
|
|
|
|
seen += [account_str]
|
|
|
|
|
distinct.sort(key=lambda address: address._key or [0])
|
|
|
|
|
return distinct
|
|
|
|
|
|
2021-09-06 09:07:26 -07:00
|
|
|
|
def observe_events(self, context: Context, interval: int = 30) -> DisposingSubject:
|
|
|
|
|
perp_event_queue: PerpEventQueue = PerpEventQueue.load(
|
|
|
|
|
context, self.underlying_perp_market.event_queue, self.lot_size_converter)
|
|
|
|
|
perp_splitter: UnseenPerpEventChangesTracker = UnseenPerpEventChangesTracker(perp_event_queue)
|
|
|
|
|
|
|
|
|
|
fill_events = DisposingSubject()
|
|
|
|
|
disposable_subscription = rx.interval(interval).pipe(
|
|
|
|
|
ops.observe_on(context.create_thread_pool_scheduler()),
|
|
|
|
|
ops.start_with(-1),
|
|
|
|
|
ops.map(lambda _: PerpEventQueue.load(
|
|
|
|
|
context, self.underlying_perp_market.event_queue, self.lot_size_converter)),
|
|
|
|
|
ops.flat_map(perp_splitter.unseen),
|
|
|
|
|
ops.catch(observable_pipeline_error_reporter),
|
|
|
|
|
ops.retry()
|
|
|
|
|
).subscribe(fill_events)
|
|
|
|
|
fill_events.add_disposable(disposable_subscription)
|
|
|
|
|
return fill_events
|
|
|
|
|
|
2021-07-23 06:18:26 -07:00
|
|
|
|
def __str__(self) -> str:
|
2021-07-28 09:43:58 -07:00
|
|
|
|
underlying: str = f"{self.underlying_perp_market}".replace("\n", "\n ")
|
2021-08-26 02:31:02 -07:00
|
|
|
|
return f"""« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_address}]
|
2021-07-28 09:43:58 -07:00
|
|
|
|
{underlying}
|
|
|
|
|
»"""
|
2021-07-23 06:18:26 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# # 🥭 PerpMarketStub class
|
2021-06-25 02:33:40 -07:00
|
|
|
|
#
|
2021-07-23 06:18:26 -07:00
|
|
|
|
# This class holds information to load a `PerpMarket` object but doesn't automatically load it.
|
|
|
|
|
#
|
|
|
|
|
class PerpMarketStub(Market):
|
2021-11-08 03:39:09 -08:00
|
|
|
|
def __init__(self, mango_program_address: PublicKey, address: PublicKey, base: Instrument, quote: Token, group_address: PublicKey):
|
2021-08-26 02:31:02 -07:00
|
|
|
|
super().__init__(mango_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
2021-07-23 06:18:26 -07:00
|
|
|
|
self.group_address: PublicKey = group_address
|
|
|
|
|
|
|
|
|
|
def load(self, context: Context, group: typing.Optional[Group] = None) -> PerpMarket:
|
|
|
|
|
actual_group: Group = group or Group.load(context, self.group_address)
|
|
|
|
|
underlying_perp_market: PerpMarketDetails = PerpMarketDetails.load(context, self.address, actual_group)
|
2021-08-26 02:31:02 -07:00
|
|
|
|
return PerpMarket(self.program_address, self.address, self.base, self.quote, underlying_perp_market)
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
2021-07-23 06:18:26 -07:00
|
|
|
|
@property
|
|
|
|
|
def symbol(self) -> str:
|
|
|
|
|
return f"{self.base.symbol}-PERP"
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
2021-07-23 06:18:26 -07:00
|
|
|
|
def __str__(self) -> str:
|
2021-08-26 02:31:02 -07:00
|
|
|
|
return f"« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_address}] »"
|