parent
3e24d01af4
commit
bad179ebaa
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -31,14 +31,14 @@ jobs:
|
|||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Cache_dependencies
|
- name: Cache_dependencies
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache
|
id: cache
|
||||||
with:
|
with:
|
||||||
path: ~/dependencies/
|
path: ~/dependencies/
|
||||||
key: ${{ runner.os }}-dependencies
|
key: ${{ runner.os }}-dependencies
|
||||||
|
|
||||||
- name: pip cache (linux)
|
- name: pip cache (linux)
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
@ -126,14 +126,14 @@ jobs:
|
|||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Cache_dependencies
|
- name: Cache_dependencies
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
id: cache
|
id: cache
|
||||||
with:
|
with:
|
||||||
path: ~/dependencies/
|
path: ~/dependencies/
|
||||||
key: ${{ runner.os }}-dependencies
|
key: ${{ runner.os }}-dependencies
|
||||||
|
|
||||||
- name: pip cache (macOS)
|
- name: pip cache (macOS)
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
with:
|
with:
|
||||||
path: ~/Library/Caches/pip
|
path: ~/Library/Caches/pip
|
||||||
@ -218,7 +218,7 @@ jobs:
|
|||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Pip cache (Windows)
|
- name: Pip cache (Windows)
|
||||||
uses: actions/cache@preview
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ~\AppData\Local\pip\Cache
|
path: ~\AppData\Local\pip\Cache
|
||||||
key: ${{ matrix.os }}-${{ matrix.python-version }}-pip
|
key: ${{ matrix.os }}-${{ matrix.python-version }}-pip
|
||||||
|
2
.github/workflows/docker_update_readme.yml
vendored
2
.github/workflows/docker_update_readme.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Docker Hub Description
|
- name: Docker Hub Description
|
||||||
uses: peter-evans/dockerhub-description@v2.4.3
|
uses: peter-evans/dockerhub-description@v3
|
||||||
env:
|
env:
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||||
DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
@ -51,9 +51,9 @@ When buying with the orderbook enabled (`bid_strategy.use_order_book=True`), Fre
|
|||||||
|
|
||||||
#### Buy price without Orderbook enabled
|
#### Buy price without Orderbook enabled
|
||||||
|
|
||||||
The following section uses `side` as the configured `bid_strategy.price_side`.
|
The following section uses `side` as the configured `bid_strategy.price_side` (defaults to `"bid"`).
|
||||||
|
|
||||||
When not using orderbook (`bid_strategy.use_order_book=False`), Freqtrade uses the best `side` price from the ticker if it's below the `last` traded price from the ticker. Otherwise (when the `side` price is above the `last` price), it calculates a rate between `side` and `last` price.
|
When not using orderbook (`bid_strategy.use_order_book=False`), Freqtrade uses the best `side` price from the ticker if it's below the `last` traded price from the ticker. Otherwise (when the `side` price is above the `last` price), it calculates a rate between `side` and `last` price based on `bid_strategy.ask_last_balance`..
|
||||||
|
|
||||||
The `bid_strategy.ask_last_balance` configuration parameter controls this. A value of `0.0` will use `side` price, while `1.0` will use the `last` price and values between those interpolate between ask and last price.
|
The `bid_strategy.ask_last_balance` configuration parameter controls this. A value of `0.0` will use `side` price, while `1.0` will use the `last` price and values between those interpolate between ask and last price.
|
||||||
|
|
||||||
@ -88,9 +88,9 @@ When selling with the orderbook enabled (`ask_strategy.use_order_book=True`), Fr
|
|||||||
|
|
||||||
#### Sell price without Orderbook enabled
|
#### Sell price without Orderbook enabled
|
||||||
|
|
||||||
When not using orderbook (`ask_strategy.use_order_book=False`), the price at the `ask_strategy.price_side` side (defaults to `"ask"`) from the ticker will be used as the sell price.
|
The following section uses `side` as the configured `ask_strategy.price_side` (defaults to `"ask"`).
|
||||||
|
|
||||||
When not using orderbook (`ask_strategy.use_order_book=False`), Freqtrade uses the best `side` price from the ticker if it's below the `last` traded price from the ticker. Otherwise (when the `side` price is above the `last` price), it calculates a rate between `side` and `last` price.
|
When not using orderbook (`ask_strategy.use_order_book=False`), Freqtrade uses the best `side` price from the ticker if it's above the `last` traded price from the ticker. Otherwise (when the `side` price is below the `last` price), it calculates a rate between `side` and `last` price based on `ask_strategy.bid_last_balance`.
|
||||||
|
|
||||||
The `ask_strategy.bid_last_balance` configuration parameter controls this. A value of `0.0` will use `side` price, while `1.0` will use the last price and values between those interpolate between `side` and last price.
|
The `ask_strategy.bid_last_balance` configuration parameter controls this. A value of `0.0` will use `side` price, while `1.0` will use the last price and values between those interpolate between `side` and last price.
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
mkdocs==1.2.3
|
mkdocs==1.3.0
|
||||||
mkdocs-material==8.2.5
|
mkdocs-material==8.2.8
|
||||||
mdx_truly_sane_lists==1.2
|
mdx_truly_sane_lists==1.2
|
||||||
pymdown-extensions==9.2
|
pymdown-extensions==9.3
|
||||||
|
jinja2==3.1.1
|
||||||
|
@ -146,7 +146,7 @@ def version(self) -> str:
|
|||||||
|
|
||||||
The strategies can be derived from other strategies. This avoids duplication of your custom strategy code. You can use this technique to override small parts of your main strategy, leaving the rest untouched:
|
The strategies can be derived from other strategies. This avoids duplication of your custom strategy code. You can use this technique to override small parts of your main strategy, leaving the rest untouched:
|
||||||
|
|
||||||
``` python
|
``` python title="user_data/strategies/myawesomestrategy.py"
|
||||||
class MyAwesomeStrategy(IStrategy):
|
class MyAwesomeStrategy(IStrategy):
|
||||||
...
|
...
|
||||||
stoploss = 0.13
|
stoploss = 0.13
|
||||||
@ -155,6 +155,10 @@ class MyAwesomeStrategy(IStrategy):
|
|||||||
# should be in any custom strategy...
|
# should be in any custom strategy...
|
||||||
...
|
...
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
``` python title="user_data/strategies/MyAwesomeStrategy2.py"
|
||||||
|
from myawesomestrategy import MyAwesomeStrategy
|
||||||
class MyAwesomeStrategy2(MyAwesomeStrategy):
|
class MyAwesomeStrategy2(MyAwesomeStrategy):
|
||||||
# Override something
|
# Override something
|
||||||
stoploss = 0.08
|
stoploss = 0.08
|
||||||
@ -163,16 +167,7 @@ class MyAwesomeStrategy2(MyAwesomeStrategy):
|
|||||||
|
|
||||||
Both attributes and methods may be overridden, altering behavior of the original strategy in a way you need.
|
Both attributes and methods may be overridden, altering behavior of the original strategy in a way you need.
|
||||||
|
|
||||||
!!! Note "Parent-strategy in different files"
|
While keeping the subclass in the same file is technically possible, it can lead to some problems with hyperopt parameter files, we therefore recommend to use separate strategy files, and import the parent strategy as shown above.
|
||||||
If you have the parent-strategy in a different file, you'll need to add the following to the top of your "child"-file to ensure proper loading, otherwise freqtrade may not be able to load the parent strategy correctly.
|
|
||||||
|
|
||||||
``` python
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
sys.path.append(str(Path(__file__).parent))
|
|
||||||
|
|
||||||
from myawesomestrategy import MyAwesomeStrategy
|
|
||||||
```
|
|
||||||
|
|
||||||
## Embedding Strategies
|
## Embedding Strategies
|
||||||
|
|
||||||
|
@ -1,27 +1,14 @@
|
|||||||
""" Freqtrade bot """
|
""" Freqtrade bot """
|
||||||
__version__ = 'develop'
|
__version__ = 'develop'
|
||||||
|
|
||||||
if __version__ == 'develop':
|
if 'dev' in __version__:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
__version__ = 'develop-' + subprocess.check_output(
|
__version__ = __version__ + '-' + subprocess.check_output(
|
||||||
['git', 'log', '--format="%h"', '-n 1'],
|
['git', 'log', '--format="%h"', '-n 1'],
|
||||||
stderr=subprocess.DEVNULL).decode("utf-8").rstrip().strip('"')
|
stderr=subprocess.DEVNULL).decode("utf-8").rstrip().strip('"')
|
||||||
|
|
||||||
# from datetime import datetime
|
|
||||||
# last_release = subprocess.check_output(
|
|
||||||
# ['git', 'tag']
|
|
||||||
# ).decode('utf-8').split()[-1].split(".")
|
|
||||||
# # Releases are in the format "2020.1" - we increment the latest version for dev.
|
|
||||||
# prefix = f"{last_release[0]}.{int(last_release[1]) + 1}"
|
|
||||||
# dev_version = int(datetime.now().timestamp() // 1000)
|
|
||||||
# __version__ = f"{prefix}.dev{dev_version}"
|
|
||||||
|
|
||||||
# subprocess.check_output(
|
|
||||||
# ['git', 'log', '--format="%h"', '-n 1'],
|
|
||||||
# stderr=subprocess.DEVNULL).decode("utf-8").rstrip().strip('"')
|
|
||||||
except Exception: # pragma: no cover
|
except Exception: # pragma: no cover
|
||||||
# git not available, ignore
|
# git not available, ignore
|
||||||
try:
|
try:
|
||||||
|
@ -6,6 +6,7 @@ This module load custom objects
|
|||||||
import importlib.util
|
import importlib.util
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, Union
|
from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, Union
|
||||||
|
|
||||||
@ -15,6 +16,22 @@ from freqtrade.exceptions import OperationalException
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PathModifier:
|
||||||
|
def __init__(self, path: Path):
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
"""Inject path to allow importing with relative imports."""
|
||||||
|
sys.path.insert(0, str(self.path))
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
"""Undo insertion of local path."""
|
||||||
|
str_path = str(self.path)
|
||||||
|
if str_path in sys.path:
|
||||||
|
sys.path.remove(str_path)
|
||||||
|
|
||||||
|
|
||||||
class IResolver:
|
class IResolver:
|
||||||
"""
|
"""
|
||||||
This class contains all the logic to load custom classes
|
This class contains all the logic to load custom classes
|
||||||
@ -57,27 +74,32 @@ class IResolver:
|
|||||||
|
|
||||||
# Generate spec based on absolute path
|
# Generate spec based on absolute path
|
||||||
# Pass object_name as first argument to have logging print a reasonable name.
|
# Pass object_name as first argument to have logging print a reasonable name.
|
||||||
spec = importlib.util.spec_from_file_location(object_name or "", str(module_path))
|
with PathModifier(module_path.parent):
|
||||||
if not spec:
|
module_name = module_path.stem or ""
|
||||||
return iter([None])
|
spec = importlib.util.spec_from_file_location(module_name, str(module_path))
|
||||||
|
if not spec:
|
||||||
module = importlib.util.module_from_spec(spec)
|
|
||||||
try:
|
|
||||||
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints
|
|
||||||
except (ModuleNotFoundError, SyntaxError, ImportError, NameError) as err:
|
|
||||||
# Catch errors in case a specific module is not installed
|
|
||||||
logger.warning(f"Could not import {module_path} due to '{err}'")
|
|
||||||
if enum_failed:
|
|
||||||
return iter([None])
|
return iter([None])
|
||||||
|
|
||||||
valid_objects_gen = (
|
module = importlib.util.module_from_spec(spec)
|
||||||
(obj, inspect.getsource(module)) for
|
try:
|
||||||
name, obj in inspect.getmembers(
|
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints
|
||||||
module, inspect.isclass) if ((object_name is None or object_name == name)
|
except (ModuleNotFoundError, SyntaxError, ImportError, NameError) as err:
|
||||||
and issubclass(obj, cls.object_type)
|
# Catch errors in case a specific module is not installed
|
||||||
and obj is not cls.object_type)
|
logger.warning(f"Could not import {module_path} due to '{err}'")
|
||||||
)
|
if enum_failed:
|
||||||
return valid_objects_gen
|
return iter([None])
|
||||||
|
|
||||||
|
valid_objects_gen = (
|
||||||
|
(obj, inspect.getsource(module)) for
|
||||||
|
name, obj in inspect.getmembers(
|
||||||
|
module, inspect.isclass) if ((object_name is None or object_name == name)
|
||||||
|
and issubclass(obj, cls.object_type)
|
||||||
|
and obj is not cls.object_type
|
||||||
|
and obj.__module__ == module_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# The __module__ check ensures we only use strategies that are defined in this folder.
|
||||||
|
return valid_objects_gen
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _search_object(cls, directory: Path, *, object_name: str, add_source: bool = False
|
def _search_object(cls, directory: Path, *, object_name: str, add_source: bool = False
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
coveralls==3.3.1
|
coveralls==3.3.1
|
||||||
flake8==4.0.1
|
flake8==4.0.1
|
||||||
flake8-tidy-imports==4.6.0
|
flake8-tidy-imports==4.6.0
|
||||||
mypy==0.940
|
mypy==0.942
|
||||||
pytest==7.1.0
|
pytest==7.1.1
|
||||||
pytest-asyncio==0.18.2
|
pytest-asyncio==0.18.3
|
||||||
pytest-cov==3.0.0
|
pytest-cov==3.0.0
|
||||||
pytest-mock==3.7.0
|
pytest-mock==3.7.0
|
||||||
pytest-random-order==1.0.4
|
pytest-random-order==1.0.4
|
||||||
@ -22,8 +22,8 @@ nbconvert==6.4.4
|
|||||||
# mypy types
|
# mypy types
|
||||||
types-cachetools==5.0.0
|
types-cachetools==5.0.0
|
||||||
types-filelock==3.2.5
|
types-filelock==3.2.5
|
||||||
types-requests==2.27.12
|
types-requests==2.27.15
|
||||||
types-tabulate==0.8.5
|
types-tabulate==0.8.6
|
||||||
|
|
||||||
# Extensions to datetime library
|
# Extensions to datetime library
|
||||||
types-python-dateutil==2.8.9
|
types-python-dateutil==2.8.10
|
@ -2,22 +2,22 @@ numpy==1.22.3
|
|||||||
pandas==1.4.1
|
pandas==1.4.1
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==1.76.5
|
ccxt==1.77.36
|
||||||
# Pin cryptography for now due to rust build errors with piwheels
|
# Pin cryptography for now due to rust build errors with piwheels
|
||||||
cryptography==36.0.1
|
cryptography==36.0.2
|
||||||
aiohttp==3.8.1
|
aiohttp==3.8.1
|
||||||
SQLAlchemy==1.4.32
|
SQLAlchemy==1.4.32
|
||||||
python-telegram-bot==13.11
|
python-telegram-bot==13.11
|
||||||
arrow==1.2.2
|
arrow==1.2.2
|
||||||
cachetools==4.2.2
|
cachetools==4.2.2
|
||||||
requests==2.27.1
|
requests==2.27.1
|
||||||
urllib3==1.26.8
|
urllib3==1.26.9
|
||||||
jsonschema==4.4.0
|
jsonschema==4.4.0
|
||||||
TA-Lib==0.4.24
|
TA-Lib==0.4.24
|
||||||
technical==1.3.0
|
technical==1.3.0
|
||||||
tabulate==0.8.9
|
tabulate==0.8.9
|
||||||
pycoingecko==2.2.0
|
pycoingecko==2.2.0
|
||||||
jinja2==3.0.3
|
jinja2==3.1.1
|
||||||
tables==3.7.0
|
tables==3.7.0
|
||||||
blosc==1.10.6
|
blosc==1.10.6
|
||||||
|
|
||||||
|
@ -1019,8 +1019,8 @@ def limit_buy_order_open():
|
|||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
'side': 'buy',
|
'side': 'buy',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
|
'timestamp': arrow.utcnow().int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
'timestamp': arrow.utcnow().int_timestamp,
|
|
||||||
'price': 0.00001099,
|
'price': 0.00001099,
|
||||||
'amount': 90.99181073,
|
'amount': 90.99181073,
|
||||||
'filled': 0.0,
|
'filled': 0.0,
|
||||||
@ -1046,6 +1046,7 @@ def market_buy_order():
|
|||||||
'type': 'market',
|
'type': 'market',
|
||||||
'side': 'buy',
|
'side': 'buy',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
|
'timestamp': arrow.utcnow().int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
'price': 0.00004099,
|
'price': 0.00004099,
|
||||||
'amount': 91.99181073,
|
'amount': 91.99181073,
|
||||||
@ -1062,6 +1063,7 @@ def market_sell_order():
|
|||||||
'type': 'market',
|
'type': 'market',
|
||||||
'side': 'sell',
|
'side': 'sell',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
|
'timestamp': arrow.utcnow().int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
'price': 0.00004173,
|
'price': 0.00004173,
|
||||||
'amount': 91.99181073,
|
'amount': 91.99181073,
|
||||||
@ -1078,7 +1080,8 @@ def limit_buy_order_old():
|
|||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
'side': 'buy',
|
'side': 'buy',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
'datetime': str(arrow.utcnow().shift(minutes=-601).datetime),
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
||||||
|
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp * 1000,
|
||||||
'price': 0.00001099,
|
'price': 0.00001099,
|
||||||
'amount': 90.99181073,
|
'amount': 90.99181073,
|
||||||
'filled': 0.0,
|
'filled': 0.0,
|
||||||
@ -1094,6 +1097,7 @@ def limit_sell_order_old():
|
|||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
'side': 'sell',
|
'side': 'sell',
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
|
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
||||||
'price': 0.00001099,
|
'price': 0.00001099,
|
||||||
'amount': 90.99181073,
|
'amount': 90.99181073,
|
||||||
@ -1110,6 +1114,7 @@ def limit_buy_order_old_partial():
|
|||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
'side': 'buy',
|
'side': 'buy',
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
|
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
||||||
'price': 0.00001099,
|
'price': 0.00001099,
|
||||||
'amount': 90.99181073,
|
'amount': 90.99181073,
|
||||||
@ -1139,7 +1144,7 @@ def limit_buy_order_canceled_empty(request):
|
|||||||
'info': {},
|
'info': {},
|
||||||
'id': '1234512345',
|
'id': '1234512345',
|
||||||
'clientOrderId': None,
|
'clientOrderId': None,
|
||||||
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp,
|
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
||||||
'lastTradeTimestamp': None,
|
'lastTradeTimestamp': None,
|
||||||
'symbol': 'LTC/USDT',
|
'symbol': 'LTC/USDT',
|
||||||
@ -1160,7 +1165,7 @@ def limit_buy_order_canceled_empty(request):
|
|||||||
'info': {},
|
'info': {},
|
||||||
'id': 'AZNPFF-4AC4N-7MKTAT',
|
'id': 'AZNPFF-4AC4N-7MKTAT',
|
||||||
'clientOrderId': None,
|
'clientOrderId': None,
|
||||||
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp,
|
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
||||||
'lastTradeTimestamp': None,
|
'lastTradeTimestamp': None,
|
||||||
'status': 'canceled',
|
'status': 'canceled',
|
||||||
@ -1181,7 +1186,7 @@ def limit_buy_order_canceled_empty(request):
|
|||||||
'info': {},
|
'info': {},
|
||||||
'id': '1234512345',
|
'id': '1234512345',
|
||||||
'clientOrderId': 'alb1234123',
|
'clientOrderId': 'alb1234123',
|
||||||
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp,
|
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
||||||
'lastTradeTimestamp': None,
|
'lastTradeTimestamp': None,
|
||||||
'symbol': 'LTC/USDT',
|
'symbol': 'LTC/USDT',
|
||||||
@ -1202,7 +1207,7 @@ def limit_buy_order_canceled_empty(request):
|
|||||||
'info': {},
|
'info': {},
|
||||||
'id': '1234512345',
|
'id': '1234512345',
|
||||||
'clientOrderId': 'alb1234123',
|
'clientOrderId': 'alb1234123',
|
||||||
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp,
|
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
||||||
'lastTradeTimestamp': None,
|
'lastTradeTimestamp': None,
|
||||||
'symbol': 'LTC/USDT',
|
'symbol': 'LTC/USDT',
|
||||||
@ -1228,7 +1233,7 @@ def limit_sell_order_open():
|
|||||||
'side': 'sell',
|
'side': 'sell',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
'timestamp': arrow.utcnow().int_timestamp,
|
'timestamp': arrow.utcnow().int_timestamp * 1000,
|
||||||
'price': 0.00001173,
|
'price': 0.00001173,
|
||||||
'amount': 90.99181073,
|
'amount': 90.99181073,
|
||||||
'filled': 0.0,
|
'filled': 0.0,
|
||||||
@ -1394,7 +1399,7 @@ def tickers():
|
|||||||
'BLK/BTC': {
|
'BLK/BTC': {
|
||||||
'symbol': 'BLK/BTC',
|
'symbol': 'BLK/BTC',
|
||||||
'timestamp': 1522014806072,
|
'timestamp': 1522014806072,
|
||||||
'datetime': '2018-03-25T21:53:26.720Z',
|
'datetime': '2018-03-25T21:53:26.072Z',
|
||||||
'high': 0.007745,
|
'high': 0.007745,
|
||||||
'low': 0.007512,
|
'low': 0.007512,
|
||||||
'bid': 0.007729,
|
'bid': 0.007729,
|
||||||
@ -1890,7 +1895,8 @@ def buy_order_fee():
|
|||||||
'type': 'limit',
|
'type': 'limit',
|
||||||
'side': 'buy',
|
'side': 'buy',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
'datetime': str(arrow.utcnow().shift(minutes=-601).datetime),
|
'timestamp': arrow.utcnow().shift(minutes=-601).int_timestamp * 1000,
|
||||||
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
||||||
'price': 0.245441,
|
'price': 0.245441,
|
||||||
'amount': 8.0,
|
'amount': 8.0,
|
||||||
'cost': 1.963528,
|
'cost': 1.963528,
|
||||||
@ -2199,7 +2205,7 @@ def limit_buy_order_usdt_open():
|
|||||||
'side': 'buy',
|
'side': 'buy',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
'timestamp': arrow.utcnow().int_timestamp,
|
'timestamp': arrow.utcnow().int_timestamp * 1000,
|
||||||
'price': 2.00,
|
'price': 2.00,
|
||||||
'amount': 30.0,
|
'amount': 30.0,
|
||||||
'filled': 0.0,
|
'filled': 0.0,
|
||||||
@ -2226,7 +2232,7 @@ def limit_sell_order_usdt_open():
|
|||||||
'side': 'sell',
|
'side': 'sell',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
'timestamp': arrow.utcnow().int_timestamp,
|
'timestamp': arrow.utcnow().int_timestamp * 1000,
|
||||||
'price': 2.20,
|
'price': 2.20,
|
||||||
'amount': 30.0,
|
'amount': 30.0,
|
||||||
'filled': 0.0,
|
'filled': 0.0,
|
||||||
@ -2251,6 +2257,7 @@ def market_buy_order_usdt():
|
|||||||
'type': 'market',
|
'type': 'market',
|
||||||
'side': 'buy',
|
'side': 'buy',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
|
'timestamp': arrow.utcnow().int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
'price': 2.00,
|
'price': 2.00,
|
||||||
'amount': 30.0,
|
'amount': 30.0,
|
||||||
@ -2307,6 +2314,7 @@ def market_sell_order_usdt():
|
|||||||
'type': 'market',
|
'type': 'market',
|
||||||
'side': 'sell',
|
'side': 'sell',
|
||||||
'symbol': 'mocked',
|
'symbol': 'mocked',
|
||||||
|
'timestamp': arrow.utcnow().int_timestamp * 1000,
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
'price': 2.20,
|
'price': 2.20,
|
||||||
'amount': 30.0,
|
'amount': 30.0,
|
||||||
|
@ -1098,7 +1098,7 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
|||||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
|
|
||||||
order = exchange.create_order(
|
order = exchange.create_order(
|
||||||
pair='ETH/BTC', ordertype=ordertype, side=side, amount=1, rate=200)
|
pair='ETH/BTC', ordertype=ordertype, side=side, amount=1, rate=rate)
|
||||||
|
|
||||||
assert 'id' in order
|
assert 'id' in order
|
||||||
assert 'info' in order
|
assert 'info' in order
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||||
|
|
||||||
import talib.abstract as ta
|
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
from strategy_test_v2 import StrategyTestV2
|
||||||
|
|
||||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||||
from freqtrade.strategy import (BooleanParameter, DecimalParameter, IntParameter, IStrategy,
|
from freqtrade.strategy import BooleanParameter, DecimalParameter, IntParameter, RealParameter
|
||||||
RealParameter)
|
|
||||||
|
|
||||||
|
|
||||||
class HyperoptableStrategy(IStrategy):
|
class HyperoptableStrategy(StrategyTestV2):
|
||||||
"""
|
"""
|
||||||
Default Strategy provided by freqtrade bot.
|
Default Strategy provided by freqtrade bot.
|
||||||
Please do not modify this strategy, it's intended for internal use only.
|
Please do not modify this strategy, it's intended for internal use only.
|
||||||
@ -16,38 +15,6 @@ class HyperoptableStrategy(IStrategy):
|
|||||||
or strategy repository https://github.com/freqtrade/freqtrade-strategies
|
or strategy repository https://github.com/freqtrade/freqtrade-strategies
|
||||||
for samples and inspiration.
|
for samples and inspiration.
|
||||||
"""
|
"""
|
||||||
INTERFACE_VERSION = 2
|
|
||||||
|
|
||||||
# Minimal ROI designed for the strategy
|
|
||||||
minimal_roi = {
|
|
||||||
"40": 0.0,
|
|
||||||
"30": 0.01,
|
|
||||||
"20": 0.02,
|
|
||||||
"0": 0.04
|
|
||||||
}
|
|
||||||
|
|
||||||
# Optimal stoploss designed for the strategy
|
|
||||||
stoploss = -0.10
|
|
||||||
|
|
||||||
# Optimal ticker interval for the strategy
|
|
||||||
timeframe = '5m'
|
|
||||||
|
|
||||||
# Optional order type mapping
|
|
||||||
order_types = {
|
|
||||||
'buy': 'limit',
|
|
||||||
'sell': 'limit',
|
|
||||||
'stoploss': 'limit',
|
|
||||||
'stoploss_on_exchange': False
|
|
||||||
}
|
|
||||||
|
|
||||||
# Number of candles the strategy requires before producing valid signals
|
|
||||||
startup_candle_count: int = 20
|
|
||||||
|
|
||||||
# Optional time in force for orders
|
|
||||||
order_time_in_force = {
|
|
||||||
'buy': 'gtc',
|
|
||||||
'sell': 'gtc',
|
|
||||||
}
|
|
||||||
|
|
||||||
buy_params = {
|
buy_params = {
|
||||||
'buy_rsi': 35,
|
'buy_rsi': 35,
|
||||||
@ -91,55 +58,6 @@ class HyperoptableStrategy(IStrategy):
|
|||||||
"""
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
||||||
"""
|
|
||||||
Adds several different TA indicators to the given DataFrame
|
|
||||||
|
|
||||||
Performance Note: For the best performance be frugal on the number of indicators
|
|
||||||
you are using. Let uncomment only the indicator you are using in your strategies
|
|
||||||
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
|
|
||||||
:param dataframe: Dataframe with data from the exchange
|
|
||||||
:param metadata: Additional information, like the currently traded pair
|
|
||||||
:return: a Dataframe with all mandatory indicators for the strategies
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Momentum Indicator
|
|
||||||
# ------------------------------------
|
|
||||||
|
|
||||||
# ADX
|
|
||||||
dataframe['adx'] = ta.ADX(dataframe)
|
|
||||||
|
|
||||||
# MACD
|
|
||||||
macd = ta.MACD(dataframe)
|
|
||||||
dataframe['macd'] = macd['macd']
|
|
||||||
dataframe['macdsignal'] = macd['macdsignal']
|
|
||||||
dataframe['macdhist'] = macd['macdhist']
|
|
||||||
|
|
||||||
# Minus Directional Indicator / Movement
|
|
||||||
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
|
||||||
|
|
||||||
# Plus Directional Indicator / Movement
|
|
||||||
dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
|
||||||
|
|
||||||
# RSI
|
|
||||||
dataframe['rsi'] = ta.RSI(dataframe)
|
|
||||||
|
|
||||||
# Stoch fast
|
|
||||||
stoch_fast = ta.STOCHF(dataframe)
|
|
||||||
dataframe['fastd'] = stoch_fast['fastd']
|
|
||||||
dataframe['fastk'] = stoch_fast['fastk']
|
|
||||||
|
|
||||||
# Bollinger bands
|
|
||||||
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
|
|
||||||
dataframe['bb_lowerband'] = bollinger['lower']
|
|
||||||
dataframe['bb_middleband'] = bollinger['mid']
|
|
||||||
dataframe['bb_upperband'] = bollinger['upper']
|
|
||||||
|
|
||||||
# EMA - Exponential Moving Average
|
|
||||||
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
|
|
||||||
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Based on TA indicators, populates the buy signal for the given dataframe
|
Based on TA indicators, populates the buy signal for the given dataframe
|
||||||
|
@ -7,7 +7,7 @@ from pandas import DataFrame
|
|||||||
|
|
||||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.strategy.interface import IStrategy
|
from freqtrade.strategy import IStrategy
|
||||||
|
|
||||||
|
|
||||||
class StrategyTestV2(IStrategy):
|
class StrategyTestV2(IStrategy):
|
||||||
|
Loading…
Reference in New Issue
Block a user