Skip to content

Commit

Permalink
[DCA] add time based DCA tests
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeDSM committed Oct 3, 2023
1 parent a1e5f88 commit 701b26f
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Trading/Mode/dca_trading_mode/dca_trading.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ async def dca_task(self):
f"configure another trigger mode to use {self.trading_mode.get_name()} in backtesting."
)
return
await asyncio.sleep(self.trading_mode.minutes_before_next_buy)
await asyncio.sleep(self.trading_mode.minutes_before_next_buy * commons_constants.MINUTE_TO_SECONDS)
except Exception as e:
self.logger.error(f"An error happened during DCA task : {e}")

Expand Down Expand Up @@ -502,7 +502,7 @@ def init_user_inputs(self, inputs: dict) -> None:
DCATradingModeProducer.TRIGGER_MODE: TriggerMode.TIME_BASED.value
}
}
)) * commons_constants.MINUTE_TO_SECONDS
))
trading_modes.user_select_order_amount(self, inputs, include_sell=False)
self.use_market_entry_orders = self.UI.user_input(
DCATradingModeConsumer.USE_MARKET_ENTRY_ORDERS, commons_enums.UserInputTypes.BOOLEAN, False, inputs,
Expand Down
60 changes: 60 additions & 0 deletions Trading/Mode/dca_trading_mode/tests/test_dca_trading_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import os.path
import mock
import decimal
import asyncio

import async_channel.util as channel_util

Expand Down Expand Up @@ -210,6 +211,65 @@ async def test_init_config_values(tools):
assert mode.stop_loss_price_multiplier == decimal.Decimal("0.1")


async def test_inner_start(tools):
mode, producer, consumer, trader = await _init_mode(tools, _get_config(tools, {}))
with mock.patch.object(producer, "dca_task", mock.AsyncMock()) as dca_task_mock, \
mock.patch.object(producer, "get_channels_registration", mock.Mock(return_value=[])):
# evaluator based
mode.trigger_mode = dca_trading.TriggerMode.MAXIMUM_EVALUATORS_SIGNALS_BASED
await producer.inner_start()
for _ in range(10):
await asyncio_tools.wait_asyncio_next_cycle()
dca_task_mock.assert_not_called()

# time based
mode.trigger_mode = dca_trading.TriggerMode.TIME_BASED
await producer.inner_start()
for _ in range(10):
await asyncio_tools.wait_asyncio_next_cycle()
dca_task_mock.assert_called_once()


async def test_dca_task(tools):
mode, producer, consumer, trader = await _init_mode(tools, _get_config(tools, {}))
calls = []
try:
def _on_trigger(**kwargs):
if len(calls):
# now stop
producer.should_stop = True
calls.append(kwargs)
producer.exchange_manager.is_backtesting = True
with mock.patch.object(asyncio, "sleep", mock.AsyncMock()) as sleep_mock:
# backtesting: trigger only once
with mock.patch.object(producer, "trigger_dca", mock.AsyncMock(side_effect=_on_trigger)) as trigger_dca_mock:
await producer.dca_task()
assert trigger_dca_mock.call_count == 1
assert trigger_dca_mock.mock_calls[0].kwargs == {
"cryptocurrency": "Bitcoin",
"symbol": "BTC/USDT",
"state": trading_enums.EvaluatorStates.VERY_LONG
}
sleep_mock.assert_not_called()

calls.clear()
# live: loop trigger
producer.exchange_manager.is_backtesting = False
with mock.patch.object(producer, "trigger_dca", mock.AsyncMock(side_effect=_on_trigger)) as trigger_dca_mock:
await producer.dca_task()
assert trigger_dca_mock.call_count == 2
assert trigger_dca_mock.mock_calls[0].kwargs == {
"cryptocurrency": "Bitcoin",
"symbol": "BTC/USDT",
"state": trading_enums.EvaluatorStates.VERY_LONG
}
assert sleep_mock.call_count == 2
assert sleep_mock.mock_calls[0].args == (10080 * commons_constants.MINUTE_TO_SECONDS, )
assert sleep_mock.mock_calls[1].args == (10080 * commons_constants.MINUTE_TO_SECONDS, )
finally:
producer.exchange_manager.is_backtesting = True


async def test_trigger_dca(tools):
update = {}
mode, producer, consumer, trader = await _init_mode(tools, _get_config(tools, update))
Expand Down

0 comments on commit 701b26f

Please sign in to comment.