Skip to content

Commit

Permalink
Merge pull request hummingbot#2956 from CoinAlpha/feat/avellaneda_sto…
Browse files Browse the repository at this point in the history
…ikov

[v0.38] feat / implement Avellaneda-Stoickov in PMM strategy
  • Loading branch information
CrimsonJacket authored Apr 2, 2021
2 parents 81e2770 + 84f175d commit 37df326
Show file tree
Hide file tree
Showing 12 changed files with 1,615 additions and 2 deletions.
2 changes: 1 addition & 1 deletion hummingbot/client/command/config_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def list_configs(self, # type: HummingbotApplication
self._notify("\n".join(lines))

if self.strategy_name is not None:
data = [[cv.key, cv.value] for cv in self.strategy_config_map.values() if not cv.is_secure]
data = [[cv.printable_key or cv.key, cv.value] for cv in self.strategy_config_map.values() if not cv.is_secure]
df = pd.DataFrame(data=data, columns=columns)
self._notify("\nStrategy Configurations:")
lines = [" " + line for line in df.to_string(index=False, max_colwidth=50).split("\n")]
Expand Down
4 changes: 3 additions & 1 deletion hummingbot/client/config/config_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def __init__(self,
# Whether to prompt a user for value when new strategy config file is created
prompt_on_new: bool = False,
# Whether this is a config var used in connect command
is_connect_key: bool = False):
is_connect_key: bool = False,
printable_key: str = None):
self.prompt = prompt
self.key = key
self.value = None
Expand All @@ -36,6 +37,7 @@ def __init__(self,
self._on_validated = on_validated
self.prompt_on_new = prompt_on_new
self.is_connect_key = is_connect_key
self.printable_key = printable_key

async def get_prompt(self):
if inspect.iscoroutinefunction(self.prompt):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from .base_trailing_indicator import BaseTrailingIndicator
import numpy as np


class AverageVolatilityIndicator(BaseTrailingIndicator):
def __init__(self, sampling_length: int = 30, processing_length: int = 15):
super().__init__(sampling_length, processing_length)

def _indicator_calculation(self) -> float:
return np.var(self._sampling_buffer.get_as_numpy_array())

def _processing_calculation(self) -> float:
processing_array = self._processing_buffer.get_as_numpy_array()
return np.sqrt(np.mean(processing_array))
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from abc import ABC, abstractmethod
import numpy as np
import logging
from ..ring_buffer import RingBuffer

pmm_logger = None


class BaseTrailingIndicator(ABC):
@classmethod
def logger(cls):
global pmm_logger
if pmm_logger is None:
pmm_logger = logging.getLogger(__name__)
return pmm_logger

def __init__(self, sampling_length: int = 30, processing_length: int = 15):
self._sampling_length = sampling_length
self._sampling_buffer = RingBuffer(sampling_length)
self._processing_length = processing_length
self._processing_buffer = RingBuffer(processing_length)

def add_sample(self, value: float):
self._sampling_buffer.add_value(value)
indicator_value = self._indicator_calculation()
self._processing_buffer.add_value(indicator_value)

@abstractmethod
def _indicator_calculation(self) -> float:
raise NotImplementedError

def _processing_calculation(self) -> float:
"""
Processing of the processing buffer to return final value.
Default behavior is buffer average
"""
return np.mean(self._processing_buffer.get_as_numpy_array())

@property
def current_value(self) -> float:
return self._processing_calculation()

@property
def is_sampling_buffer_full(self) -> bool:
return self._sampling_buffer.is_full

@property
def is_processing_buffer_full(self) -> bool:
return self._processing_buffer.is_full
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from base_trailing_indicator import BaseTrailingIndicator
import pandas as pd


class ExponentialMovingAverageIndicator(BaseTrailingIndicator):
def __init__(self, sampling_length: int = 30, processing_length: int = 1):
if processing_length != 1:
raise Exception("Exponential moving average processing_length should be 1")
super().__init__(sampling_length, processing_length)

def _indicator_calculation(self) -> float:
ema = pd.Series(self._sampling_buffer.get_as_numpy_array())\
.ewm(span=self._sampling_length, adjust=True).mean()
return ema[-1]

def _processing_calculation(self) -> float:
return self._processing_buffer.get_last_value()
6 changes: 6 additions & 0 deletions hummingbot/strategy/avellaneda_market_making/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env python

from .avellaneda_market_making import AvellanedaMarketMakingStrategy
__all__ = [
AvellanedaMarketMakingStrategy,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# distutils: language=c++

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


cdef class AvellanedaMarketMakingStrategy(StrategyBase):
cdef:
object _market_info
object _minimum_spread
object _order_amount
double _order_refresh_time
double _max_order_age
object _order_refresh_tolerance_pct
double _filled_order_delay
object _inventory_target_base_pct
bint _order_optimization_enabled
bint _add_transaction_costs_to_orders
bint _hb_app_notification
bint _is_debug

double _cancel_timestamp
double _create_timestamp
object _limit_order_type
bint _all_markets_ready
int _filled_buys_balance
int _filled_sells_balance
double _last_timestamp
double _status_report_interval
int64_t _logging_options
object _last_own_trade_price
int _volatility_sampling_period
double _last_sampling_timestamp
bint _parameters_based_on_spread
int _ticks_to_be_ready
object _min_spread
object _max_spread
object _vol_to_spread_multiplier
object _inventory_risk_aversion
object _kappa
object _gamma
object _eta
object _closing_time
object _time_left
object _q_adjustment_factor
object _reserved_price
object _optimal_spread
object _optimal_bid
object _optimal_ask
object _latest_parameter_calculation_vol
str _debug_csv_path
object _avg_vol

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_price_modifiers(self, object proposal)
cdef c_apply_order_amount_eta_transformation(self, object proposal)
cdef c_apply_budget_constraint(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_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)
cdef double c_get_spread(self)
cdef c_collect_market_variables(self, double timestamp)
cdef bint c_is_algorithm_ready(self)
cdef c_calculate_reserved_price_and_optimal_spread(self)
cdef object c_calculate_target_inventory(self)
cdef c_recalculate_parameters(self)
Loading

0 comments on commit 37df326

Please sign in to comment.