Skip to content

Commit

Permalink
Re-Import Orig PMM
Browse files Browse the repository at this point in the history
  • Loading branch information
TheHolyRoger committed Feb 24, 2021
1 parent d0fe3e5 commit 95f9c2a
Show file tree
Hide file tree
Showing 19 changed files with 2,276 additions and 1 deletion.
14 changes: 14 additions & 0 deletions hummingbot/strategy/pure_market_making/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env python

from .pure_market_making import PureMarketMakingStrategy
from .asset_price_delegate import AssetPriceDelegate
from .order_book_asset_price_delegate import OrderBookAssetPriceDelegate
from .api_asset_price_delegate import APIAssetPriceDelegate
from .inventory_cost_price_delegate import InventoryCostPriceDelegate
__all__ = [
PureMarketMakingStrategy,
AssetPriceDelegate,
OrderBookAssetPriceDelegate,
APIAssetPriceDelegate,
InventoryCostPriceDelegate,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .asset_price_delegate cimport AssetPriceDelegate

cdef class APIAssetPriceDelegate(AssetPriceDelegate):
cdef object _custom_api_feed
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from .asset_price_delegate cimport AssetPriceDelegate
from hummingbot.data_feed.custom_api_data_feed import CustomAPIDataFeed, NetworkStatus

cdef class APIAssetPriceDelegate(AssetPriceDelegate):
def __init__(self, api_url: str):
super().__init__()
self._custom_api_feed = CustomAPIDataFeed(api_url=api_url)
self._custom_api_feed.start()

cdef object c_get_mid_price(self):
return self._custom_api_feed.get_price()

@property
def ready(self) -> bool:
return self._custom_api_feed.network_status == NetworkStatus.CONNECTED

@property
def custom_api_feed(self) -> CustomAPIDataFeed:
return self._custom_api_feed
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

cdef class AssetPriceDelegate:
cdef object c_get_mid_price(self)
16 changes: 16 additions & 0 deletions hummingbot/strategy/pure_market_making/asset_price_delegate.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from decimal import Decimal


cdef class AssetPriceDelegate:
# The following exposed Python functions are meant for unit tests
# ---------------------------------------------------------------
def get_mid_price(self) -> Decimal:
return self.c_get_mid_price()
# ---------------------------------------------------------------

cdef object c_get_mid_price(self):
raise NotImplementedError

@property
def ready(self) -> bool:
raise NotImplementedError
55 changes: 55 additions & 0 deletions hummingbot/strategy/pure_market_making/data_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env python
from typing import (
NamedTuple,
List
)
from decimal import Decimal
from hummingbot.core.event.events import OrderType

ORDER_PROPOSAL_ACTION_CREATE_ORDERS = 1
ORDER_PROPOSAL_ACTION_CANCEL_ORDERS = 1 << 1


class OrdersProposal(NamedTuple):
actions: int
buy_order_type: OrderType
buy_order_prices: List[Decimal]
buy_order_sizes: List[Decimal]
sell_order_type: OrderType
sell_order_prices: List[Decimal]
sell_order_sizes: List[Decimal]
cancel_order_ids: List[str]


class PricingProposal(NamedTuple):
buy_order_prices: List[Decimal]
sell_order_prices: List[Decimal]


class SizingProposal(NamedTuple):
buy_order_sizes: List[Decimal]
sell_order_sizes: List[Decimal]


class InventorySkewBidAskRatios(NamedTuple):
bid_ratio: float
ask_ratio: float


class PriceSize:
def __init__(self, price: Decimal, size: Decimal):
self.price: Decimal = price
self.size: Decimal = size

def __repr__(self):
return f"[ p: {self.price} s: {self.size} ]"


class Proposal:
def __init__(self, buys: List[PriceSize], sells: List[PriceSize]):
self.buys: List[PriceSize] = buys
self.sells: List[PriceSize] = sells

def __repr__(self):
return f"{len(self.buys)} buys: {', '.join([str(o) for o in self.buys])} " \
f"{len(self.sells)} sells: {', '.join([str(o) for o in self.sells])}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from decimal import Decimal, InvalidOperation
from typing import Optional

from hummingbot.core.event.events import OrderFilledEvent, TradeType
from hummingbot.model.inventory_cost import InventoryCost
from hummingbot.model.sql_connection_manager import SQLConnectionManager

s_decimal_0 = Decimal("0")


class InventoryCostPriceDelegate:
def __init__(self, sql: SQLConnectionManager, trading_pair: str) -> None:
self.base_asset, self.quote_asset = trading_pair.split("-")
self._session = sql.get_shared_session()

@property
def ready(self) -> bool:
return True

def get_price(self) -> Optional[Decimal]:
record = InventoryCost.get_record(
self._session, self.base_asset, self.quote_asset
)

if record is None or record.base_volume is None or record.base_volume is None:
return None

try:
price = record.quote_volume / record.base_volume
except InvalidOperation:
# decimal.InvalidOperation: [<class 'decimal.DivisionUndefined'>] - both volumes are 0
return None
return Decimal(price)

def process_order_fill_event(self, fill_event: OrderFilledEvent) -> None:
base_asset, quote_asset = fill_event.trading_pair.split("-")
quote_volume = fill_event.amount * fill_event.price
base_volume = fill_event.amount

for fee_asset, fee_amount in fill_event.trade_fee.flat_fees:
if fill_event.trade_type == TradeType.BUY:
if fee_asset == base_asset:
base_volume -= fee_amount
elif fee_asset == quote_asset:
quote_volume += fee_amount
else:
# Ok, some other asset used (like BNB), assume that we paid in base asset for simplicity
base_volume /= 1 + fill_event.trade_fee.percent
else:
if fee_asset == base_asset:
base_volume += fee_amount
elif fee_asset == quote_asset:
# TODO: with new logic, this quote volume adjustment does not impacts anything
quote_volume -= fee_amount
else:
# Ok, some other asset used (like BNB), assume that we paid in base asset for simplicity
base_volume /= 1 + fill_event.trade_fee.percent

if fill_event.trade_type == TradeType.SELL:
record = InventoryCost.get_record(self._session, base_asset, quote_asset)
if not record:
raise RuntimeError("Sold asset without having inventory price set. This should not happen.")

# We're keeping initial buy price intact. Profits are not changing inventory price intentionally.
quote_volume = -(Decimal(record.quote_volume / record.base_volume) * base_volume)
base_volume = -base_volume

InventoryCost.add_volume(
self._session, base_asset, quote_asset, base_volume, quote_volume
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cdef object c_calculate_bid_ask_ratios_from_base_asset_ratio(double base_asset_amount,
double quote_asset_amount,
double price,
double target_base_asset_ratio,
double base_asset_range)
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from decimal import Decimal
import numpy as np

from .data_types import InventorySkewBidAskRatios

decimal_0 = Decimal(0)
decimal_1 = Decimal(1)
decimal_2 = Decimal(2)


def calculate_total_order_size(order_start_size: Decimal, order_step_size: Decimal = decimal_0,
order_levels: int = 1) -> Decimal:
order_levels_decimal = order_levels
return (decimal_2 *
(order_levels_decimal * order_start_size +
order_levels_decimal * (order_levels_decimal - decimal_1) / decimal_2 * order_step_size
)
)


def calculate_bid_ask_ratios_from_base_asset_ratio(
base_asset_amount: float, quote_asset_amount: float, price: float,
target_base_asset_ratio: float, base_asset_range: float) -> InventorySkewBidAskRatios:
return c_calculate_bid_ask_ratios_from_base_asset_ratio(base_asset_amount,
quote_asset_amount,
price,
target_base_asset_ratio,
base_asset_range)


cdef object c_calculate_bid_ask_ratios_from_base_asset_ratio(
double base_asset_amount, double quote_asset_amount, double price,
double target_base_asset_ratio, double base_asset_range):
cdef:
double total_portfolio_value = base_asset_amount * price + quote_asset_amount

if total_portfolio_value <= 0.0 or base_asset_range <= 0.0:
return InventorySkewBidAskRatios(0.0, 0.0)

cdef:
double base_asset_value = base_asset_amount * price
double base_asset_range_value = min(base_asset_range * price, total_portfolio_value * 0.5)
double target_base_asset_value = total_portfolio_value * target_base_asset_ratio
double left_base_asset_value_limit = max(target_base_asset_value - base_asset_range_value, 0.0)
double right_base_asset_value_limit = target_base_asset_value + base_asset_range_value
double left_inventory_ratio = np.interp(base_asset_value,
[left_base_asset_value_limit, target_base_asset_value],
[0.0, 0.5])
double right_inventory_ratio = np.interp(base_asset_value,
[target_base_asset_value, right_base_asset_value_limit],
[0.5, 1.0])
double bid_adjustment = (np.interp(left_inventory_ratio, [0, 0.5], [2.0, 1.0])
if base_asset_value < target_base_asset_value
else np.interp(right_inventory_ratio, [0.5, 1], [1.0, 0.0]))
double ask_adjustment = 2.0 - bid_adjustment

return InventorySkewBidAskRatios(bid_adjustment, ask_adjustment)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .asset_price_delegate cimport AssetPriceDelegate
from hummingbot.connector.exchange_base cimport ExchangeBase

cdef class OrderBookAssetPriceDelegate(AssetPriceDelegate):
cdef:
ExchangeBase _market
str _trading_pair
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from hummingbot.core.event.events import PriceType
from .asset_price_delegate cimport AssetPriceDelegate
from hummingbot.connector.exchange_base import ExchangeBase
from decimal import Decimal

cdef class OrderBookAssetPriceDelegate(AssetPriceDelegate):
def __init__(self, market: ExchangeBase, trading_pair: str):
super().__init__()
self._market = market
self._trading_pair = trading_pair

cdef object c_get_mid_price(self):
return (self._market.c_get_price(self._trading_pair, True) +
self._market.c_get_price(self._trading_pair, False))/Decimal('2')

@property
def ready(self) -> bool:
return self._market.ready

def get_price_by_type(self, price_type: PriceType) -> Decimal:
return self._market.get_price_by_type(self._trading_pair, price_type)

@property
def market(self) -> ExchangeBase:
return self._market

@property
def trading_pair(self) -> str:
return self._trading_pair
78 changes: 78 additions & 0 deletions hummingbot/strategy/pure_market_making/pure_market_making.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# distutils: language=c++

from libc.stdint cimport int64_t
from hummingbot.strategy.strategy_base cimport StrategyBase


cdef class PureMarketMakingStrategy(StrategyBase):
cdef:
object _market_info

object _bid_spread
object _ask_spread
object _minimum_spread
object _order_amount
int _order_levels
int _buy_levels
int _sell_levels
object _order_level_spread
object _order_level_amount
double _order_refresh_time
double _max_order_age
object _order_refresh_tolerance_pct
double _filled_order_delay
bint _inventory_skew_enabled
object _inventory_target_base_pct
object _inventory_range_multiplier
bint _hanging_orders_enabled
object _hanging_orders_cancel_pct
bint _order_optimization_enabled
object _ask_order_optimization_depth
object _bid_order_optimization_depth
bint _add_transaction_costs_to_orders
object _asset_price_delegate
object _inventory_cost_price_delegate
object _price_type
bint _take_if_crossed
object _price_ceiling
object _price_floor
bint _ping_pong_enabled
list _ping_pong_warning_lines
bint _hb_app_notification
object _order_override

double _cancel_timestamp
double _create_timestamp
object _limit_order_type
bint _all_markets_ready
int _filled_buys_balance
int _filled_sells_balance
list _hanging_order_ids
double _last_timestamp
double _status_report_interval
int64_t _logging_options
object _last_own_trade_price
list _hanging_aged_order_prices

cdef object c_get_mid_price(self)
cdef object c_create_base_proposal(self)
cdef tuple c_get_adjusted_available_balance(self, list orders)
cdef c_apply_order_levels_modifiers(self, object proposal)
cdef c_apply_price_band(self, object proposal)
cdef c_apply_ping_pong(self, object proposal)
cdef c_apply_order_price_modifiers(self, object proposal)
cdef c_apply_order_size_modifiers(self, object proposal)
cdef c_apply_inventory_skew(self, object proposal)
cdef c_apply_budget_constraint(self, object proposal)

cdef c_filter_out_takers(self, object proposal)
cdef c_apply_order_optimization(self, object proposal)
cdef c_apply_add_transaction_costs(self, object proposal)
cdef bint c_is_within_tolerance(self, list current_prices, list proposal_prices)
cdef c_cancel_active_orders(self, object proposal)
cdef c_cancel_hanging_orders(self)
cdef c_cancel_orders_below_min_spread(self)
cdef c_aged_order_refresh(self)
cdef bint c_to_create_orders(self, object proposal)
cdef c_execute_orders_proposal(self, object proposal)
cdef set_timers(self)
Loading

0 comments on commit 95f9c2a

Please sign in to comment.