Merge branch 'develop' into StopLossSupport

This commit is contained in:
Gert 2018-06-01 19:34:39 -07:00
commit c9708442ff
20 changed files with 462 additions and 57 deletions

1
.gitignore vendored
View File

@ -6,7 +6,6 @@ config*.json
.hyperopt
logfile.txt
hyperopt_trials.pickle
user_data/
freqtrade-plot.html
# Byte-compiled / optimized / DLL files

View File

@ -56,24 +56,19 @@ Windows, macOS and Linux
- [x] **Persistence**: Persistence is achieved through sqlite
- [x] **Dry-run**: Run the bot without playing money.
- [x] **Backtesting**: Run a simulation of your buy/sell strategy.
- [x] **Strategy Optimization**: Optimize your buy/sell strategy
parameters with Hyperopts.
- [x] **Whitelist crypto-currencies**: Select which crypto-currency you
want to trade.
- [x] **Blacklist crypto-currencies**: Select which crypto-currency you
want to avoid.
- [x] **Strategy Optimization by machine learning**: Use machine learning to optimize your buy/sell
strategy parameters with real exchange data.
- [x] **Whitelist crypto-currencies**: Select which crypto-currency you want to trade.
- [x] **Blacklist crypto-currencies**: Select which crypto-currency you want to avoid.
- [x] **Manageable via Telegram**: Manage the bot with Telegram
- [x] **Display profit/loss in fiat**: Display your profit/loss in
33 fiat.
- [x] **Daily summary of profit/loss**: Provide a daily summary
of your profit/loss.
- [x] **Performance status report**: Provide a performance status of
your current trades.
- [x] **Display profit/loss in fiat**: Display your profit/loss in 33 fiat.
- [x] **Daily summary of profit/loss**: Provide a daily summary of your profit/loss.
- [x] **Performance status report**: Provide a performance status of your current trades.
### Exchange supported
- [x] Bittrex
- [ ] Binance
- [ ] Others
### Exchange marketplaces supported
- [X] [Bittrex](https://bittrex.com/)
- [X] [Binance](https://www.binance.com/)
- [ ] [113 others to tests](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_
## Quick start
This quick start section is a very short explanation on how to test the
@ -144,8 +139,9 @@ to understand the requirements before sending your pull-requests.
### Bot commands
```bash
usage: main.py [-h] [-v] [--version] [-c PATH] [--dry-run-db] [--datadir PATH]
[--dynamic-whitelist [INT]]
usage: main.py [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME]
[--strategy-path PATH] [--dynamic-whitelist [INT]]
[--dry-run-db]
{backtesting,hyperopt} ...
Simple High Frequency Trading Bot for crypto currencies
@ -161,13 +157,18 @@ optional arguments:
--version show program's version number and exit
-c PATH, --config PATH
specify configuration file (default: config.json)
--dry-run-db Force dry run to use a local DB
"tradesv3.dry_run.sqlite" instead of memory DB. Work
only if dry_run is enabled.
--datadir PATH path to backtest data (default freqdata/tests/testdata
-d PATH, --datadir PATH
path to backtest data (default:
freqtrade/tests/testdata
-s NAME, --strategy NAME
specify strategy class name (default: DefaultStrategy)
--strategy-path PATH specify additional strategy lookup path
--dynamic-whitelist [INT]
dynamically generate and update whitelist based on 24h
BaseVolume (Default 20 currencies)
--dry-run-db Force dry run to use a local DB
"tradesv3.dry_run.sqlite" instead of memory DB. Work
only if dry_run is enabled.
```
More details on:
- [How to run the bot](https://github.com/gcarq/freqtrade/blob/develop/docs/bot-usage.md#bot-commands)

View File

@ -53,9 +53,9 @@ python3 ./freqtrade/main.py backtesting --datadir freqtrade/tests/testdata-20180
**With a (custom) strategy file**
```bash
python3 ./freqtrade/main.py -s currentstrategy backtesting
python3 ./freqtrade/main.py -s TestStrategy backtesting
```
Where `-s currentstrategy` refers to a filename `currentstrategy.py` in `freqtrade/user_data/strategies`
Where `-s TestStrategy` refers to the class name within the strategy file `test_strategy.py` found in the `freqtrade/user_data/strategies` directory
**Exporting trades to file**
```bash

View File

@ -9,7 +9,8 @@ it.
## Bot commands
```
usage: main.py [-h] [-c PATH] [-v] [--version] [--dynamic-whitelist [INT]]
usage: main.py [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME]
[--strategy-path PATH] [--dynamic-whitelist [INT]]
[--dry-run-db]
{backtesting,hyperopt} ...
@ -26,17 +27,18 @@ optional arguments:
--version show program's version number and exit
-c PATH, --config PATH
specify configuration file (default: config.json)
-d PATH, --datadir PATH
path to backtest data (default:
freqtrade/tests/testdata
-s NAME, --strategy NAME
specify strategy class name (default: DefaultStrategy)
--strategy-path PATH specify additional strategy lookup path
--dry-run-db Force dry run to use a local DB
"tradesv3.dry_run.sqlite" instead of memory DB. Work
only if dry_run is enabled.
--datadir PATH
path to backtest data (default freqdata/tests/testdata
--dynamic-whitelist [INT]
dynamically generate and update whitelist based on 24h
BaseVolume (Default 20 currencies)
--dry-run-db Force dry run to use a local DB
"tradesv3.dry_run.sqlite" instead of memory DB. Work
only if dry_run is enabled.
```
### How to use a different config file?

View File

@ -24,7 +24,7 @@ The table below will list all configuration parameters.
| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file.
| `unfilledtimeout` | 0 | No | How long (in minutes) the bot will wait for an unfilled order to complete, after which the order will be cancelled.
| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below.
| `exchange.name` | bittrex | Yes | Name of the exchange class to use.
| `exchange.name` | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
| `exchange.key` | key | No | API key to use for the exchange. Only required when you are in production mode.
| `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode.
| `exchange.pair_whitelist` | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param.
@ -79,6 +79,18 @@ use the `last` price and values between those interpolate between ask and last
price. Using `ask` price will guarantee quick success in bid, but bot will also
end up paying more then would probably have been necessary.
### What values for exchange.name?
Freqtrade is based on [CCXT library](https://github.com/ccxt/ccxt) that supports 115 cryptocurrency
exchange markets and trading APIs. The complete up-to-date list can be found in the
[CCXT repo homepage](https://github.com/ccxt/ccxt/tree/master/python). However, the bot was tested
with only Bittrex and Binance.
The bot was tested with the following exchanges:
- [Bittrex](https://bittrex.com/): "bittrex"
- [Binance](https://www.binance.com/): "binance"
Feel free to test other exchanges and submit your PR to improve the bot.
### What values for fiat_display_currency?
`fiat_display_currency` set the fiat to use for the conversion form coin to fiat in Telegram.
The valid value are: "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD".
@ -96,7 +108,7 @@ creating trades.
"dry_run": true,
```
3. Remove your Bittrex API key (change them by fake api credentials)
3. Remove your Exchange API key (change them by fake api credentials)
```json
"exchange": {
"name": "bittrex",
@ -122,7 +134,7 @@ you run it in production mode.
"dry_run": false,
```
3. Insert your Bittrex API key (change them by fake api keys)
3. Insert your Exchange API key (change them by fake api keys)
```json
"exchange": {
"name": "bittrex",

View File

@ -132,6 +132,13 @@ You can run a one-off container that is immediately deleted upon exiting with th
docker run --rm -v /etc/localtime:/etc/localtime:ro -v `pwd`/config.json:/freqtrade/config.json -it freqtrade
```
There is known issue in OSX Docker versions after 17.09.1, whereby /etc/localtime cannot be shared causing Docker to not start. A work-around for this is to start with the following cmd.
```bash
docker run --rm -e TZ=`ls -la /etc/localtime | cut -d/ -f8-9` -v `pwd`/config.json:/freqtrade/config.json -it freqtrade
```
More information on this docker issue and work-around can be read here: https://github.com/docker/for-mac/issues/2396
In this example, the database will be created inside the docker instance and will be lost when you will refresh your image.

View File

@ -8,6 +8,7 @@ import time
from typing import Dict
from coinmarketcap import Market
from requests.exceptions import RequestException
logger = logging.getLogger(__name__)
@ -94,8 +95,8 @@ class CryptoToFiatConverter(object):
coinlistings = self._coinmarketcap.listings()
self._cryptomap = dict(map(lambda coin: (coin["symbol"], str(coin["id"])),
coinlistings["data"]))
except ValueError:
logger.error("Could not load FIAT Cryptocurrency map")
except (ValueError, RequestException) as e:
logger.error("Could not load FIAT Cryptocurrency map for the following problem: %s", e)
def convert_amount(self, crypto_amount: float, crypto_symbol: str, fiat_symbol: str) -> float:
"""

View File

@ -61,6 +61,7 @@ def set_loggers() -> None:
:return: None
"""
logging.getLogger('requests.packages.urllib3').setLevel(logging.INFO)
logging.getLogger('ccxt.base.exchange').setLevel(logging.INFO)
logging.getLogger('telegram').setLevel(logging.INFO)

View File

@ -29,7 +29,7 @@ def trim_tickerlist(tickerlist: List[Dict], timerange: Tuple[Tuple, int, int]) -
if stype[0] == 'index':
start_index = start
elif stype[0] == 'date':
while tickerlist[start_index][0] < start * 1000:
while start_index < len(tickerlist) and tickerlist[start_index][0] < start * 1000:
start_index += 1
if stype[1] == 'line':
@ -37,7 +37,7 @@ def trim_tickerlist(tickerlist: List[Dict], timerange: Tuple[Tuple, int, int]) -
if stype[1] == 'index':
stop_index = stop
elif stype[1] == 'date':
while tickerlist[stop_index-1][0] > stop * 1000:
while stop_index > 0 and tickerlist[stop_index-1][0] > stop * 1000:
stop_index -= 1
if start_index > stop_index:
@ -100,15 +100,11 @@ def load_data(datadir: str,
for pair in _pairs:
pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange)
if not pairdata:
# download the tickerdata from exchange
download_backtesting_testdata(datadir,
pair=pair,
tick_interval=ticker_interval,
timerange=timerange)
# and retry reading the pair
pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange)
result[pair] = pairdata
if pairdata:
result[pair] = pairdata
else:
logger.warn('No data for pair %s, use --update-pairs-cached to download the data', pair)
return result

View File

@ -98,10 +98,11 @@ class RPC(object):
trade.id,
trade.pair,
shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)),
'{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate))
'{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate)),
'{:.6f}'.format(trade.amount * current_rate)
])
columns = ['ID', 'Pair', 'Since', 'Profit']
columns = ['ID', 'Pair', 'Since', 'Profit', 'Value']
df_statuses = DataFrame.from_records(trades_list, columns=columns)
df_statuses = df_statuses.set_index(columns[0])
# The style used throughout is to return a tuple

View File

@ -99,7 +99,20 @@ def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog) -> None:
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
_backup_file(file)
optimize.load_data(None, ticker_interval='1m', pairs=['MEME/BTC'])
# do not download a new pair if refresh_pairs isn't set
optimize.load_data(None,
ticker_interval='1m',
refresh_pairs=False,
pairs=['MEME/BTC'])
assert os.path.isfile(file) is False
assert log_has('No data for pair MEME/BTC, use --update-pairs-cached to download the data',
caplog.record_tuples)
# download a new pair if refresh_pairs is set
optimize.load_data(None,
ticker_interval='1m',
refresh_pairs=True,
pairs=['MEME/BTC'])
assert os.path.isfile(file) is True
assert log_has('Download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
_clean_test_file(file)

View File

@ -106,6 +106,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
assert 'just now' in result['Since'].all()
assert 'ETH/BTC' in result['Pair'].all()
assert '-0.59%' in result['Profit'].all()
assert 'Value' in result
def test_rpc_daily_profit(default_conf, update, ticker, fee,

View File

@ -6,6 +6,8 @@ from unittest.mock import MagicMock
import pytest
from requests.exceptions import RequestException
from freqtrade.fiat_convert import CryptoFiat, CryptoToFiatConverter
from freqtrade.tests.conftest import patch_coinmarketcap
@ -133,6 +135,21 @@ def test_loadcryptomap(mocker):
assert fiat_convert._cryptomap["BTC"] == "1"
def test_fiat_init_network_exception(mocker):
# Because CryptoToFiatConverter is a Singleton we reset the listings
listmock = MagicMock(side_effect=RequestException)
mocker.patch.multiple(
'freqtrade.fiat_convert.Market',
listings=listmock,
)
# with pytest.raises(RequestEsxception):
fiat_convert = CryptoToFiatConverter()
fiat_convert._cryptomap = {}
fiat_convert._load_cryptomap()
assert len(fiat_convert._cryptomap) == 0
def test_fiat_convert_without_network():
# Because CryptoToFiatConverter is a Singleton we reset the value of _coinmarketcap

View File

@ -1,5 +1,5 @@
ccxt==1.14.24
SQLAlchemy==1.2.7
ccxt==1.14.96
SQLAlchemy==1.2.8
python-telegram-bot==10.1.0
arrow==0.12.1
cachetools==2.1.0
@ -12,7 +12,7 @@ scipy==1.1.0
jsonschema==2.6.0
numpy==1.14.3
TA-Lib==0.4.17
pytest==3.5.1
pytest==3.6.0
pytest-mock==1.10.0
pytest-cov==2.5.1
hyperopt==0.1

View File

@ -159,6 +159,15 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
fillcolor="rgba(0,176,246,0.2)",
line={'color': "transparent"},
)
bb_middle = go.Scatter(
x=data.date,
y=data.bb_middleband,
name='BB middle',
fill="tonexty",
fillcolor="rgba(0,176,246,0.2)",
line={'color': "red"},
)
macd = go.Scattergl(x=data['date'], y=data['macd'], name='MACD')
macdsignal = go.Scattergl(x=data['date'], y=data['macdsignal'], name='MACD signal')
volume = go.Bar(x=data['date'], y=data['volume'], name='Volume')
@ -173,7 +182,9 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
fig.append_trace(candles, 1, 1)
fig.append_trace(bb_lower, 1, 1)
fig.append_trace(bb_middle, 1, 1)
fig.append_trace(bb_upper, 1, 1)
fig.append_trace(buys, 1, 1)
fig.append_trace(sells, 1, 1)
fig.append_trace(volume, 2, 1)

View File

@ -89,13 +89,21 @@ function config_generator () {
echo "General configuration"
echo "-------------------------"
echo
read -p "Max open trades: (Default: 3) " max_trades
default_max_trades=3
read -p "Max open trades: (Default: $default_max_trades) " max_trades
max_trades=${max_trades:-$default_max_trades}
read -p "Stake amount: (Default: 0.05) " stake_amount
default_stake_amount=0.05
read -p "Stake amount: (Default: $default_stake_amount) " stake_amount
stake_amount=${stake_amount:-$default_stake_amount}
read -p "Stake currency: (Default: BTC) " stake_currency
default_stake_currency="BTC"
read -p "Stake currency: (Default: $default_stake_currency) " stake_currency
stake_currency=${stake_currency:-$default_stake_currency}
read -p "Fiat currency: (Default: USD) " fiat_currency
default_fiat_currency="USD"
read -p "Fiat currency: (Default: $default_fiat_currency) " fiat_currency
fiat_currency=${fiat_currency:-$default_fiat_currency}
echo "------------------------"
echo "Bittrex config generator"

View File

@ -0,0 +1,94 @@
# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from typing import Dict, List
from hyperopt import hp
from functools import reduce
from pandas import DataFrame
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy # noqa
class Long(IStrategy):
"""
author@: Gert Wohlgemuth
"""
# Minimal ROI designed for the strategy.
# This attribute will be overridden if the config file contains "minimal_roi"
minimal_roi = {
"60": 0.05,
"30": 0.06,
"20": 0.07,
"0": 0.08
}
# Optimal stoploss designed for the strategy
# This attribute will be overridden if the config file contains "stoploss"
stoploss = -0.15
# Optimal ticker interval for the strategy
ticker_interval = 60
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
dataframe['cci'] = ta.CCI(dataframe)
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=50)
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']
# RSI
dataframe['rsi'] = ta.RSI(dataframe)
# Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
rsi = 0.1 * (dataframe['rsi'] - 50)
dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1)
# SAR Parabol
dataframe['sar'] = ta.SAR(dataframe)
return dataframe
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.loc[
(
(dataframe['macd'] > dataframe['macdsignal']) &
(dataframe['macd'] > 0) &
(dataframe['cci'] <= 0.0)
),
'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.loc[
(
# (dataframe['tema'] < dataframe['close'])
(dataframe['sar'] > dataframe['close']) &
(dataframe['fisher_rsi'] > 0.3)
),
'sell'] = 1
return dataframe

View File

@ -0,0 +1,75 @@
# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from typing import Dict, List
from hyperopt import hp
from functools import reduce
from pandas import DataFrame
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
class Quickie(IStrategy):
"""
author@: Gert Wohlgemuth
idea:
momentum based strategie. The main idea is that it closes trades very quickly, while avoiding excessive losses. Hence a rather moderate stop loss in this case
"""
# Minimal ROI designed for the strategy.
# This attribute will be overridden if the config file contains "minimal_roi"
minimal_roi = {
"60": 0.005,
"10": 0.01,
}
# Optimal stoploss designed for the strategy
# This attribute will be overridden if the config file contains "stoploss"
stoploss = -0.25
# Optimal ticker interval for the strategy
ticker_interval = 5
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
dataframe['adx'] = ta.ADX(dataframe)
dataframe['sma_200'] = ta.SMA(dataframe, timeperiod=200)
dataframe['sma_50'] = ta.SMA(dataframe, timeperiod=50)
# required for graphing
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_middleband'] = bollinger['mid']
dataframe['bb_upperband'] = bollinger['upper']
return dataframe
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
dataframe.loc[
(
(
(dataframe['adx'] > 30) &
(dataframe['tema'] < dataframe['bb_middleband']) &
(dataframe['tema'] > dataframe['tema'].shift(1)) &
(dataframe['sma_200'] > dataframe['close'])
)
),
'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
dataframe.loc[
(
(
(dataframe['adx'] > 70) &
(dataframe['tema'] > dataframe['bb_middleband']) &
(dataframe['tema'] < dataframe['tema'].shift(1))
)
),
'sell'] = 1
return dataframe

View File

@ -0,0 +1,76 @@
# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from typing import Dict, List
from hyperopt import hp
from functools import reduce
from pandas import DataFrame
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
class Simple(IStrategy):
"""
author@: Gert Wohlgemuth
idea:
this strategy is based on the book, 'The Simple Strategy' and can be found in detail here:
https://www.amazon.com/Simple-Strategy-Powerful-Trading-Futures-ebook/dp/B00E66QPCG/ref=sr_1_1?ie=UTF8&qid=1525202675&sr=8-1&keywords=the+simple+strategy
"""
# Minimal ROI designed for the strategy.
# since this strategy is planned around 5 minutes, we assume any time we have a 5% profit we should call it a day
# This attribute will be overridden if the config file contains "minimal_roi"
minimal_roi = {
"0": 0.01
}
# Optimal stoploss designed for the strategy
# This attribute will be overridden if the config file contains "stoploss"
stoploss = -0.25
# Optimal ticker interval for the strategy
ticker_interval = 5
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
# MACD
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
# RSI
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=7)
# required for graphing
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=12, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_upperband'] = bollinger['upper']
dataframe['bb_middleband'] = bollinger['mid']
return dataframe
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
dataframe.loc[
(
(
(dataframe['macd'] > 0) # over 0
& (dataframe['macd'] > dataframe['macdsignal']) # over signal
& (dataframe['bb_upperband'] > dataframe['bb_upperband'].shift(1)) # pointed up
& (dataframe['rsi'] > 70) # optional filter, need to investigate
)
),
'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
# different strategy used for sell points, due to be able to duplicate it to 100%
dataframe.loc[
(
(dataframe['rsi'] > 80)
),
'sell'] = 1
return dataframe

View File

@ -0,0 +1,90 @@
# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from typing import Dict, List
from hyperopt import hp
from functools import reduce
from pandas import DataFrame
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
class ZLC(IStrategy):
"""
author@: Gert Wohlgemuth
"""
# Minimal ROI designed for the strategy.
# This attribute will be overridden if the config file contains "minimal_roi"
minimal_roi = {
"60": 0.01,
"30": 0.03,
"20": 0.04,
"0": 0.01
}
# Optimal stoploss designed for the strategy
# This attribute will be overridden if the config file contains "stoploss"
stoploss = -0.3
# Optimal ticker interval for the strategy
ticker_interval = 5
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
dataframe['cci-slow'] = ta.CCI(dataframe, timeperiod=25)
dataframe['cci-fast'] = ta.CCI(dataframe, timeperiod=50)
dataframe['expo'] = ta.EMA(dataframe, timeperiod=35)
# required for graphing
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']
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
return dataframe
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.loc[
(
#don't buy on peak tops
(dataframe['close'] < dataframe['bb_middleband'])
# this is the main concept of evaluating buys
& (dataframe['cci-fast'] > 0)
& (dataframe['cci-slow'] > 0)
& (dataframe['close'] > dataframe['expo'])
)
,
'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.loc[
(dataframe['close'] >= dataframe['bb_upperband']) |
(
(dataframe['cci-fast'] < 0)
& (dataframe['cci-slow'] < 0)
& (dataframe['close'] < dataframe['expo'])
)
,
'sell'] = 0
return dataframe