Skip to content

Commit

Permalink
Improve CatchTheWave Strategy
Browse files Browse the repository at this point in the history
- Use bot's local memory for tracking
- Add parameter to use the matrix's time resolution
  • Loading branch information
math-a3k committed Aug 12, 2023
1 parent 637938d commit 31dc3ad
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 5 deletions.
51 changes: 50 additions & 1 deletion base/strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ class CatchTheWave(TradingStrategy):
def __init__(self, bot, symbol=None, **kwargs):
self.bot = bot
self.symbol = symbol or self.bot.symbol
self.local_memory = self.bot.get_local_memory(self.symbol)
self.scg = self.symbol.others["scg"]
self.diff_sm = self.scg["line_diff_sm"] # Short - Middle tendency
self.s_var = self.scg["line_s_var"] # Var of the Short tendency
Expand All @@ -182,13 +183,21 @@ def __init__(self, bot, symbol=None, **kwargs):
self.onset_periods = int(kwargs.get("onset_periods", "2"))
self.maxima_tol = Decimal(kwargs.get("maxima_tol", "0.1"))
self.sell_safeguard = Decimal(kwargs.get("sell_safeguard", "0.3"))
self.use_local_memory = bool(int(kwargs.get("use_local_memory", "1")))
self.use_matrix_time_res = bool(
int(kwargs.get("use_matrix_time_res", "0"))
)

def evaluate_buy(self):
if self.use_matrix_time_res and self.time_safeguard:
return (False, "Holding - Using matrix's time resolution...")
if self.is_on_good_status() and self.is_on_wave_onset():
return True, None
return (False, "Symbol is not in good status and ascending...")

def evaluate_sell(self):
if self.use_matrix_time_res and self.time_safeguard:
return (False, "Holding - Using matrix's time resolution")
if self.bot.price_current > self.get_min_selling_threshold():
if self.sell_on_maxima and self.is_local_maxima():
return True, None
Expand All @@ -202,6 +211,8 @@ def evaluate_sell(self):
)

def evaluate_jump(self):
if self.use_matrix_time_res and self.time_safeguard:
return False, None
symbols_with_siblings = self.get_symbols_with_siblings()
symbols = self.bot.symbol._meta.concrete_model.objects.top_symbols()
if self.early_onset:
Expand Down Expand Up @@ -243,16 +254,54 @@ def is_on_good_status(self):
return self.scg["current_good"]

def is_local_maxima(self):
if self.local_memory_available():
return self.local_memory["price_var"][-1] < -self.maxima_tol
return self.s_var[-1] * 100 < -self.maxima_tol

def is_on_wave(self):
return self.diff_sm[-1] > 0

def is_on_wave_onset(self):
if self.local_memory_available():
return self.all_positive(
self.local_memory["price_var"][-self.onset_periods :]
)
return self.all_positive(self.s_var[-self.onset_periods :])

def not_decreasing(self, line):
return all([l * 100 > -self.maxima_tol for l in line])
return all([p * 100 > -self.maxima_tol for p in line])

def get_min_selling_threshold(self):
return self.bot.price_buying * (1 + self.sell_safeguard / 100)

def local_memory_update(self):
if self.use_matrix_time_res and self.time_safeguard:
return
lm = self.bot.get_local_memory()
if not lm:
lm = {"price": [], "price_var": []}
price, price_var = lm["price"], lm["price_var"]
if self.bot.price_current:
price = price + [float(self.bot.price_current)]
if len(price) > 1:
last_var = (Decimal(price[-1]) / Decimal(price[-2]) - 1) * 100
price_var = price_var + [float(last_var)]
lm = {
"price": price[-100:],
"price_var": price_var[-100:],
}
self.local_memory = lm
self.bot.set_local_memory(self.symbol, lm)

def has_local_memory(self, symbol=None):
symbol = symbol or self.symbol
lm = self.bot.get_local_memory(symbol)
price = lm.get("price", [])
if len(price) > 1:
return True
return False

def local_memory_available(self):
return self.use_local_memory and self.bot.has_local_memory(
self.symbol, strategy=self
)
81 changes: 77 additions & 4 deletions base/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ def setUpClass(cls):
user=cls.user1,
group=cls.group1,
strategy="acmadness",
strategy_params="microgain=0.3",
strategy_params="",
fund_quote_asset_initial=10,
is_dummy=True,
is_jumpy=True,
Expand Down Expand Up @@ -1084,7 +1084,7 @@ def test_catchthewave(self):
json={"symbol": "S1BUSD", "price": "1.0"},
)
self.bot1.strategy = "catchthewave"
self.bot1.strategy_params = ""
self.bot1.strategy_params = "use_local_memory=0"
#
self.s1.others["scg"]["current_good"] = True
self.s1.others["scg"]["line_s_var"] = [1, 1, 1]
Expand All @@ -1100,7 +1100,7 @@ def test_catchthewave(self):
self.s1.others["scg"]["line_diff_sm"] = [
0,
]
self.bot1.strategy_params = "sell_on_maxima=0"
self.bot1.strategy_params = "use_local_memory=0,sell_on_maxima=0"
self.bot1.decide()
self.assertIn("Sold", self.bot1.others["last_logs"][-1])
self.bot1.buy()
Expand All @@ -1123,16 +1123,89 @@ def test_catchthewave(self):
self.assertIn(
"no other symbol to go", self.bot1.others["last_logs"][-1]
)
self.bot1.strategy_params = "early_onset=1"
self.bot1.strategy_params = "use_local_memory=0,early_onset=1"
self.bot1.symbol.others["scg"]["early_onset"] = True
self.bot1.symbol.others["scg"]["line_l_var"] = [0.1, 0, -0.11]
self.s1.others["scg"]["current_good"] = False
self.s1.others["scg"]["early_onset"] = True
self.s1.others["scg"]["line_l_var"] = [1, 1, 2]
self.s1.others["scg"]["line_s_var"] = [1, 1, 2]
self.s1.save()
self.bot1.decide()
self.assertIn("Jumped", self.bot1.others["last_logs"][-2])
self.assertIn("Bought", self.bot1.others["last_logs"][-1])
#
self.bot1.strategy_params = "use_local_memory=1"
self.bot1.reset()
self.bot1.on()
self.bot1.symbol = self.s1
#
self.s1.others["scg"]["current_good"] = True
self.s1.others["scg"]["line_s_var"] = [1, 1, 1]
self.s1.others["scg"]["line_diff_sm"] = [1, 1, 1]
self.assertEqual(self.bot1.has_local_memory(), False)
self.bot1.decide()
self.assertIn("Bought", self.bot1.others["last_logs"][-1])
self.assertEqual(self.bot1.has_local_memory(), False)
self.bot1.price_current = 1
self.bot1.decide()
self.assertEqual(self.bot1.has_local_memory(), False)
self.assertIn("below min", self.bot1.others["last_logs"][-1])
self.bot1.price_current = 1.001
self.bot1.decide()
self.assertEqual(self.bot1.has_local_memory(), True)
self.assertIn("below min", self.bot1.others["last_logs"][-1])
self.bot1.price_current = 1.3
self.bot1.decide()
self.assertIn("Kept goin", self.bot1.others["last_logs"][-1])
self.bot1.price_current = 1.299
self.bot1.decide()
self.assertIn("Kept goin", self.bot1.others["last_logs"][-1])
self.bot1.price_current = 1.2
self.bot1.decide()
self.assertIn("Sold", self.bot1.others["last_logs"][-1])
self.bot1.price_current = 1.5
self.bot1.decide()
self.assertIn(
"not in good status and ascending",
self.bot1.others["last_logs"][-1],
)
self.bot1.price_current = 1.51
self.bot1.decide()
self.assertIn("Bought", self.bot1.others["last_logs"][-1])
self.s1.others["scg"]["line_diff_sm"] = [1, 1, -0.1]
self.bot1.price_current = 1.6
self.bot1.decide()
self.assertIn("Sold", self.bot1.others["last_logs"][-1])
self.s2.others["scg"]["current_good"] = True
self.s2.save()
self.bot1.decide()
self.assertIn("Jumped", self.bot1.others["last_logs"][-2])
self.assertIn("Bought", self.bot1.others["last_logs"][-1])
self.assertEqual(self.bot1.has_local_memory(), False)
#
self.bot1.strategy_params = (
"use_local_memory=1,use_matrix_time_res=1"
)
#
self.bot1.price_current = 1.3
self.bot1.decide()
self.assertIn("Kept goin", self.bot1.others["last_logs"][-1])
self.bot1.symbol.last_updated = (
timezone.now() - timezone.timedelta(minutes=2)
)
self.bot1.price_current = 1.1
self.bot1.decide()
self.assertIn(
"Using matrix's time resolution",
self.bot1.others["last_logs"][-1],
)
self.bot1.sell()
self.bot1.decide()
self.assertIn(
"Using matrix's time resolution",
self.bot1.others["last_logs"][-1],
)


@pytest.mark.usefixtures("celery_session_app")
Expand Down
8 changes: 8 additions & 0 deletions docs/trading_bots.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ Given a Symbol, the bot will buy when it is on "good status" while selling when

*Good Status* is either when Symbol is at "Current Good" and the short-term tendency is increasing ("wave onset") or at "Early Onset" and the long-term line is not decreasing.

This strategy uses the bots' local memory to track the price movement of the asset at its native time resolution (:setting:`TIME_INTERVAL_BOTS`) while using the indicators from the matrix (also known as *Rainha Botzinha*) at her time resolution (:setting:`TIME_INTERVAL`) to forsee the market situation.

Parameters
^^^^^^^^^^

Expand All @@ -83,6 +85,12 @@ Parameters
``sell_safeguard``
Extra percentage of the buying price to set the min. selling threshold for automatic selling in the worst case scenario (``Decimal``, defaults to ``0.3``).

``use_local_memory``
Use bot's local memory (``1`` | ``0``, defaults to ``1``)

``use_matrix_time_res``
Use matrix's time resolution (:settings:`TIME_INTERVAL`) (``1`` | ``0``, defaults to ``0``)


.. rubric:: References
.. [1] .. autoclass:: base.models.TraderoBot
Expand Down

0 comments on commit 31dc3ad

Please sign in to comment.