Extract global cached_grouped_trades_per_pair into IStrategy

This commit is contained in:
Joe Schr
2024-07-04 17:12:30 +02:00
parent e5221bded7
commit 3486327e78
3 changed files with 38 additions and 32 deletions
+12 -16
View File
@@ -7,20 +7,18 @@ import time
import typing import typing
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime from datetime import datetime
from typing import Dict, Tuple from typing import Tuple
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from freqtrade.constants import DEFAULT_ORDERFLOW_COLUMNS from freqtrade.constants import DEFAULT_ORDERFLOW_COLUMNS
from freqtrade.enums import RunMode
from freqtrade.exceptions import DependencyException from freqtrade.exceptions import DependencyException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Global cache dictionary
cached_grouped_trades_per_pair: Dict[str, OrderedDict[Tuple[datetime, datetime], pd.DataFrame]] = {}
def _init_dataframe_with_trades_columns(dataframe: pd.DataFrame): def _init_dataframe_with_trades_columns(dataframe: pd.DataFrame):
""" """
@@ -64,8 +62,11 @@ def _calculate_ohlcv_candle_start_and_end(df: pd.DataFrame, timeframe: str):
def populate_dataframe_with_trades( def populate_dataframe_with_trades(
pair: str, config, dataframe: pd.DataFrame, trades: pd.DataFrame cached_grouped_trades: OrderedDict[Tuple[datetime, datetime], pd.DataFrame],
): config,
dataframe: pd.DataFrame,
trades: pd.DataFrame,
) -> Tuple[pd.DataFrame, OrderedDict[Tuple[datetime, datetime], pd.DataFrame]]:
""" """
Populates a dataframe with trades Populates a dataframe with trades
:param dataframe: Dataframe to populate :param dataframe: Dataframe to populate
@@ -74,15 +75,11 @@ def populate_dataframe_with_trades(
""" """
timeframe = config["timeframe"] timeframe = config["timeframe"]
config_orderflow = config["orderflow"] config_orderflow = config["orderflow"]
cache_size = config_orderflow["cache_size"]
# create columns for trades # create columns for trades
_init_dataframe_with_trades_columns(dataframe) _init_dataframe_with_trades_columns(dataframe)
try: try:
cached_grouped_trades: OrderedDict[Tuple[datetime, datetime], pd.DataFrame] = (
cached_grouped_trades_per_pair.get(pair, OrderedDict())
)
start_time = time.time() start_time = time.time()
# calculate ohlcv candle start and end # calculate ohlcv candle start and end
_calculate_ohlcv_candle_start_and_end(trades, timeframe) _calculate_ohlcv_candle_start_and_end(trades, timeframe)
@@ -184,7 +181,10 @@ def populate_dataframe_with_trades(
) )
# Maintain cache size # Maintain cache size
if len(cached_grouped_trades) > cache_size: if (
config.get("runmode") in (RunMode.DRY_RUN, RunMode.LIVE)
and len(cached_grouped_trades) > config_orderflow["cache_size"]
):
cached_grouped_trades.popitem(last=False) cached_grouped_trades.popitem(last=False)
else: else:
logger.debug(f"Found NO candles for trades starting with {candle_start}") logger.debug(f"Found NO candles for trades starting with {candle_start}")
@@ -196,16 +196,12 @@ def populate_dataframe_with_trades(
dataframe["imbalances"] = imbalances_series dataframe["imbalances"] = imbalances_series
dataframe["stacked_imbalances_bid"] = stacked_imbalances_bid_series dataframe["stacked_imbalances_bid"] = stacked_imbalances_bid_series
dataframe["stacked_imbalances_ask"] = stacked_imbalances_ask_series dataframe["stacked_imbalances_ask"] = stacked_imbalances_ask_series
# dereference old cache
if pair in cached_grouped_trades_per_pair:
del cached_grouped_trades_per_pair[pair]
cached_grouped_trades_per_pair[pair] = cached_grouped_trades
except Exception as e: except Exception as e:
logger.exception("Error populating dataframe with trades") logger.exception("Error populating dataframe with trades")
raise DependencyException(e) raise DependencyException(e)
return dataframe return dataframe, cached_grouped_trades
def trades_to_volumeprofile_with_total_delta_bid_ask( def trades_to_volumeprofile_with_total_delta_bid_ask(
+18 -1
View File
@@ -5,6 +5,7 @@ This module defines the interface to apply for strategies
import logging import logging
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections import OrderedDict
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from math import isinf, isnan from math import isinf, isnan
from typing import Dict, List, Optional, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
@@ -140,6 +141,11 @@ class IStrategy(ABC, HyperStrategyMixin):
# A self set parameter that represents the market direction. filled from configuration # A self set parameter that represents the market direction. filled from configuration
market_direction: MarketDirection = MarketDirection.NONE market_direction: MarketDirection = MarketDirection.NONE
# Global cache dictionary
_cached_grouped_trades_per_pair: Dict[
str, OrderedDict[Tuple[datetime, datetime], DataFrame]
] = {}
def __init__(self, config: Config) -> None: def __init__(self, config: Config) -> None:
self.config = config self.config = config
# Dict to determine if analysis is necessary # Dict to determine if analysis is necessary
@@ -1603,8 +1609,19 @@ class IStrategy(ABC, HyperStrategyMixin):
config = self.config config = self.config
config["timeframe"] = self.timeframe config["timeframe"] = self.timeframe
pair = metadata["pair"]
# TODO: slice trades to size of dataframe for faster backtesting # TODO: slice trades to size of dataframe for faster backtesting
dataframe = populate_dataframe_with_trades(metadata["pair"], config, dataframe, trades) cached_grouped_trades: OrderedDict[Tuple[datetime, datetime], DataFrame] = (
self._cached_grouped_trades_per_pair.get(pair, OrderedDict())
)
dataframe, cached_grouped_trades = populate_dataframe_with_trades(
cached_grouped_trades, config, dataframe, trades
)
# dereference old cache
if pair in self._cached_grouped_trades_per_pair:
del self._cached_grouped_trades_per_pair[pair]
self._cached_grouped_trades_per_pair[pair] = cached_grouped_trades
logger.debug("Populated dataframe with trades.") logger.debug("Populated dataframe with trades.")
+8 -15
View File
@@ -1,3 +1,5 @@
from collections import OrderedDict
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import pytest import pytest
@@ -47,15 +49,6 @@ def public_trades_list_simple(testdatadir):
return read_csv(testdatadir / "orderflow/public_trades_list_simple_example.csv").copy() return read_csv(testdatadir / "orderflow/public_trades_list_simple_example.csv").copy()
@pytest.fixture
def reset_cache(request):
import freqtrade.data.converter.orderflow as orderflow
global orderflow # noqa F811
orderflow.cached_grouped_trades_per_pair = {}
yield
def test_public_trades_columns_before_change( def test_public_trades_columns_before_change(
populate_dataframe_with_trades_dataframe, populate_dataframe_with_trades_trades populate_dataframe_with_trades_dataframe, populate_dataframe_with_trades_trades
): ):
@@ -80,7 +73,7 @@ def test_public_trades_columns_before_change(
def test_public_trades_mock_populate_dataframe_with_trades__check_orderflow( def test_public_trades_mock_populate_dataframe_with_trades__check_orderflow(
reset_cache, populate_dataframe_with_trades_dataframe, populate_dataframe_with_trades_trades populate_dataframe_with_trades_dataframe, populate_dataframe_with_trades_trades
): ):
""" """
Tests the `populate_dataframe_with_trades` function's order flow calculation. Tests the `populate_dataframe_with_trades` function's order flow calculation.
@@ -108,7 +101,7 @@ def test_public_trades_mock_populate_dataframe_with_trades__check_orderflow(
}, },
} }
# Apply the function to populate the data frame with order flow data # Apply the function to populate the data frame with order flow data
df = populate_dataframe_with_trades("BTC/UDST", config, dataframe, trades) df, _ = populate_dataframe_with_trades(OrderedDict(), config, dataframe, trades)
# Extract results from the first row of the DataFrame # Extract results from the first row of the DataFrame
results = df.iloc[0] results = df.iloc[0]
t = results["trades"] t = results["trades"]
@@ -179,7 +172,7 @@ def test_public_trades_mock_populate_dataframe_with_trades__check_orderflow(
def test_public_trades_trades_mock_populate_dataframe_with_trades__check_trades( def test_public_trades_trades_mock_populate_dataframe_with_trades__check_trades(
reset_cache, populate_dataframe_with_trades_dataframe, populate_dataframe_with_trades_trades populate_dataframe_with_trades_dataframe, populate_dataframe_with_trades_trades
): ):
""" """
Tests the `populate_dataframe_with_trades` function's handling of trades, Tests the `populate_dataframe_with_trades` function's handling of trades,
@@ -221,7 +214,7 @@ def test_public_trades_trades_mock_populate_dataframe_with_trades__check_trades(
} }
# Populate the DataFrame with trades and order flow data # Populate the DataFrame with trades and order flow data
df = populate_dataframe_with_trades("BTC/UDST", config, dataframe, trades) df, _ = populate_dataframe_with_trades(OrderedDict(), config, dataframe, trades)
# --- DataFrame and Trade Data Validation --- # --- DataFrame and Trade Data Validation ---
@@ -387,8 +380,8 @@ def test_public_trades_config_max_trades(
}, },
} }
df = populate_dataframe_with_trades( df, _ = populate_dataframe_with_trades(
"BTC/UDST", default_conf | orderflow_config, dataframe, trades OrderedDict(), default_conf | orderflow_config, dataframe, trades
) )
assert df.delta.count() == 1 assert df.delta.count() == 1