Merge branch 'develop' into StopLossSupport
This commit is contained in:
commit
c9708442ff
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,7 +6,6 @@ config*.json
|
||||
.hyperopt
|
||||
logfile.txt
|
||||
hyperopt_trials.pickle
|
||||
user_data/
|
||||
freqtrade-plot.html
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
|
45
README.md
45
README.md
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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",
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
16
setup.sh
16
setup.sh
@ -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"
|
||||
|
94
user_data/strategies/Long.py
Normal file
94
user_data/strategies/Long.py
Normal 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
|
75
user_data/strategies/Quickie.py
Normal file
75
user_data/strategies/Quickie.py
Normal 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
|
76
user_data/strategies/Simple.py
Normal file
76
user_data/strategies/Simple.py
Normal 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
|
90
user_data/strategies/ZLC.py
Normal file
90
user_data/strategies/ZLC.py
Normal 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
|
Loading…
Reference in New Issue
Block a user