diff --git a/build_helpers/publish_docker_arm64.sh b/build_helpers/publish_docker_arm64.sh index 071eb0fa2..f3cedff2e 100755 --- a/build_helpers/publish_docker_arm64.sh +++ b/build_helpers/publish_docker_arm64.sh @@ -70,20 +70,21 @@ docker push ${CACHE_IMAGE}:$TAG_ARM # Otherwise installation might fail. echo "create manifests" -docker manifest create --amend ${IMAGE_NAME}:${TAG} ${CACHE_IMAGE}:${TAG_ARM} ${IMAGE_NAME}:${TAG_PI} ${CACHE_IMAGE}:${TAG} +docker manifest create ${IMAGE_NAME}:${TAG} ${CACHE_IMAGE}:${TAG} ${CACHE_IMAGE}:${TAG_ARM} ${IMAGE_NAME}:${TAG_PI} docker manifest push -p ${IMAGE_NAME}:${TAG} -docker manifest create ${IMAGE_NAME}:${TAG_PLOT} ${CACHE_IMAGE}:${TAG_PLOT_ARM} ${CACHE_IMAGE}:${TAG_PLOT} +docker manifest create ${IMAGE_NAME}:${TAG_PLOT} ${CACHE_IMAGE}:${TAG_PLOT} ${CACHE_IMAGE}:${TAG_PLOT_ARM} docker manifest push -p ${IMAGE_NAME}:${TAG_PLOT} -docker manifest create ${IMAGE_NAME}:${TAG_FREQAI} ${CACHE_IMAGE}:${TAG_FREQAI_ARM} ${CACHE_IMAGE}:${TAG_FREQAI} +docker manifest create ${IMAGE_NAME}:${TAG_FREQAI} ${CACHE_IMAGE}:${TAG_FREQAI} ${CACHE_IMAGE}:${TAG_FREQAI_ARM} docker manifest push -p ${IMAGE_NAME}:${TAG_FREQAI} -docker manifest create ${IMAGE_NAME}:${TAG_FREQAI_RL} ${CACHE_IMAGE}:${TAG_FREQAI_RL_ARM} ${CACHE_IMAGE}:${TAG_FREQAI_RL} +docker manifest create ${IMAGE_NAME}:${TAG_FREQAI_RL} ${CACHE_IMAGE}:${TAG_FREQAI_RL} ${CACHE_IMAGE}:${TAG_FREQAI_RL_ARM} docker manifest push -p ${IMAGE_NAME}:${TAG_FREQAI_RL} # Tag as latest for develop builds if [ "${TAG}" = "develop" ]; then + echo 'Tagging image as latest' docker manifest create ${IMAGE_NAME}:latest ${CACHE_IMAGE}:${TAG_ARM} ${IMAGE_NAME}:${TAG_PI} ${CACHE_IMAGE}:${TAG} docker manifest push -p ${IMAGE_NAME}:latest fi diff --git a/build_helpers/publish_docker_multi.sh b/build_helpers/publish_docker_multi.sh index a608c1282..3e5e61564 100755 --- a/build_helpers/publish_docker_multi.sh +++ b/build_helpers/publish_docker_multi.sh @@ -26,7 +26,10 @@ if [ "${GITHUB_EVENT_NAME}" = "schedule" ]; then --cache-to=type=registry,ref=${CACHE_TAG} \ -f docker/Dockerfile.armhf \ --platform ${PI_PLATFORM} \ - -t ${IMAGE_NAME}:${TAG_PI} --push . + -t ${IMAGE_NAME}:${TAG_PI} \ + --push \ + --provenance=false \ + . else echo "event ${GITHUB_EVENT_NAME}: building with cache" # Build regular image @@ -35,12 +38,16 @@ else # Pull last build to avoid rebuilding the whole image # docker pull --platform ${PI_PLATFORM} ${IMAGE_NAME}:${TAG} + # disable provenance due to https://github.com/docker/buildx/issues/1509 docker buildx build \ --cache-from=type=registry,ref=${CACHE_TAG} \ --cache-to=type=registry,ref=${CACHE_TAG} \ -f docker/Dockerfile.armhf \ --platform ${PI_PLATFORM} \ - -t ${IMAGE_NAME}:${TAG_PI} --push . + -t ${IMAGE_NAME}:${TAG_PI} \ + --push \ + --provenance=false \ + . fi if [ $? -ne 0 ]; then @@ -68,12 +75,10 @@ fi docker images -docker push ${CACHE_IMAGE} +docker push ${CACHE_IMAGE}:$TAG docker push ${CACHE_IMAGE}:$TAG_PLOT docker push ${CACHE_IMAGE}:$TAG_FREQAI docker push ${CACHE_IMAGE}:$TAG_FREQAI_RL -docker push ${CACHE_IMAGE}:$TAG - docker images diff --git a/docs/freqai-reinforcement-learning.md b/docs/freqai-reinforcement-learning.md index 4442a2f4f..5c9733403 100644 --- a/docs/freqai-reinforcement-learning.md +++ b/docs/freqai-reinforcement-learning.md @@ -34,7 +34,7 @@ Setting up and running a Reinforcement Learning model is the same as running a R freqtrade trade --freqaimodel ReinforcementLearner --strategy MyRLStrategy --config config.json ``` -where `ReinforcementLearner` will use the templated `ReinforcementLearner` from `freqai/prediction_models/ReinforcementLearner` (or a custom user defined one located in `user_data/freqaimodels`). The strategy, on the other hand, follows the same base [feature engineering](freqai-feature-engineering.md) with `feature_engineering_*` as a typical Regressor. The difference lies in the creation of the targets, Reinforcement Learning doesnt require them. However, FreqAI requires a default (neutral) value to be set in the action column: +where `ReinforcementLearner` will use the templated `ReinforcementLearner` from `freqai/prediction_models/ReinforcementLearner` (or a custom user defined one located in `user_data/freqaimodels`). The strategy, on the other hand, follows the same base [feature engineering](freqai-feature-engineering.md) with `feature_engineering_*` as a typical Regressor. The difference lies in the creation of the targets, Reinforcement Learning doesn't require them. However, FreqAI requires a default (neutral) value to be set in the action column: ```python def set_freqai_targets(self, dataframe, **kwargs): @@ -52,18 +52,18 @@ where `ReinforcementLearner` will use the templated `ReinforcementLearner` from """ # For RL, there are no direct targets to set. This is filler (neutral) # until the agent sends an action. - df["&-action"] = 0 + dataframe["&-action"] = 0 ``` Most of the function remains the same as for typical Regressors, however, the function above shows how the strategy must pass the raw price data to the agent so that it has access to raw OHLCV in the training environment: ```python - def feature_engineering_standard(): + def feature_engineering_standard(self, dataframe, **kwargs): # The following features are necessary for RL models - informative[f"%-raw_close"] = informative["close"] - informative[f"%-raw_open"] = informative["open"] - informative[f"%-raw_high"] = informative["high"] - informative[f"%-raw_low"] = informative["low"] + dataframe[f"%-raw_close"] = dataframe["close"] + dataframe[f"%-raw_open"] = dataframe["open"] + dataframe[f"%-raw_high"] = dataframe["high"] + dataframe[f"%-raw_low"] = dataframe["low"] ``` Finally, there is no explicit "label" to make - instead it is necessary to assign the `&-action` column which will contain the agent's actions when accessed in `populate_entry/exit_trends()`. In the present example, the neutral action to 0. This value should align with the environment used. FreqAI provides two environments, both use 0 as the neutral action. @@ -243,7 +243,6 @@ FreqAI also provides a built in episodic summary logger called `self.tensorboard !!! Note The `self.tensorboard_log()` function is designed for tracking incremented objects only i.e. events, actions inside the training environment. If the event of interest is a float, the float can be passed as the second argument e.g. `self.tensorboard_log("float_metric1", 0.23)` would add 0.23 to `float_metric`. In this case you can also disable incrementing using `inc=False` parameter. - ### Choosing a base environment FreqAI provides three base environments, `Base3ActionRLEnvironment`, `Base4ActionEnvironment` and `Base5ActionEnvironment`. As the names imply, the environments are customized for agents that can select from 3, 4 or 5 actions. The `Base3ActionEnvironment` is the simplest, the agent can select from hold, long, or short. This environment can also be used for long-only bots (it automatically follows the `can_short` flag from the strategy), where long is the enter condition and short is the exit condition. Meanwhile, in the `Base4ActionEnvironment`, the agent can enter long, enter short, hold neutral, or exit position. Finally, in the `Base5ActionEnvironment`, the agent has the same actions as Base4, but instead of a single exit action, it separates exit long and exit short. The main changes stemming from the environment selection include: diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 75da3ddfd..daeccb216 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -146,7 +146,7 @@ class Order(_DECL_BASE): # Assign funding fee up to this point # (represents the funding fee since the last order) self.funding_fee = self.trade.funding_fees - if (order.get('filled', 0.0) or 0.0) > 0: + if (order.get('filled', 0.0) or 0.0) > 0 and not self.order_filled_date: self.order_filled_date = datetime.now(timezone.utc) self.order_update_date = datetime.now(timezone.utc) diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index e12e919fc..d06f05179 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -1868,7 +1868,10 @@ def test_get_exit_order_count(fee, is_short): @pytest.mark.usefixtures("init_persistence") -def test_update_order_from_ccxt(caplog): +def test_update_order_from_ccxt(caplog, time_machine): + start = datetime(2023, 1, 1, 4, tzinfo=timezone.utc) + time_machine.move_to(start, tick=False) + # Most basic order return (only has orderid) o = Order.parse_from_ccxt_object({'id': '1234'}, 'ADA/USDT', 'buy', 20.01, 1234.6) assert isinstance(o, Order) @@ -1917,7 +1920,9 @@ def test_update_order_from_ccxt(caplog): assert o.filled == 20.0 assert o.remaining == 0.0 assert not o.ft_is_open - assert o.order_filled_date is not None + assert o.order_filled_date == start + # Move time + time_machine.move_to(start + timedelta(hours=1), tick=False) ccxt_order.update({'id': 'somethingelse'}) with pytest.raises(DependencyException, match=r"Order-id's don't match"): @@ -1930,6 +1935,12 @@ def test_update_order_from_ccxt(caplog): # Call regular update - shouldn't fail. Order.update_orders([o], {'id': '1234'}) + assert o.order_filled_date == start + + # Fill order again - shouldn't update filled date + ccxt_order.update({'id': '1234'}) + Order.update_orders([o], ccxt_order) + assert o.order_filled_date == start @pytest.mark.usefixtures("init_persistence")