Merge branch 'wohlgemuth' into nullartHFT
This commit is contained in:
commit
6ddbfc3aa4
@ -40,7 +40,7 @@ due to demand, it is possible to have a default stop loss, when you are in the r
|
|||||||
the system will utilize a new stop loss, which can be a different value. For example your default stop loss is 5%, but once you are in the
|
the system will utilize a new stop loss, which can be a different value. For example your default stop loss is 5%, but once you are in the
|
||||||
black, it will be changed to be only a 1% stop loss
|
black, it will be changed to be only a 1% stop loss
|
||||||
|
|
||||||
this can be configured in the main confiuration file, the following way:
|
this can be configured in the main configuration file, the following way:
|
||||||
|
|
||||||
```
|
```
|
||||||
"trailing_stop": {
|
"trailing_stop": {
|
||||||
|
@ -65,7 +65,7 @@ class Analyze(object):
|
|||||||
frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle
|
frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
def populate_indicators(self, dataframe: DataFrame, pair: str = None) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Adds several different TA indicators to the given DataFrame
|
Adds several different TA indicators to the given DataFrame
|
||||||
|
|
||||||
@ -73,23 +73,23 @@ class Analyze(object):
|
|||||||
you are using. Let uncomment only the indicator you are using in your strategies
|
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.
|
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
|
||||||
"""
|
"""
|
||||||
return self.strategy.populate_indicators(dataframe=dataframe)
|
return self.strategy.advise_indicators(dataframe=dataframe, pair=pair)
|
||||||
|
|
||||||
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
|
def populate_buy_trend(self, dataframe: DataFrame, pair: str = None) -> 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
|
||||||
:param dataframe: DataFrame
|
:param dataframe: DataFrame
|
||||||
:return: DataFrame with buy column
|
:return: DataFrame with buy column
|
||||||
"""
|
"""
|
||||||
return self.strategy.populate_buy_trend(dataframe=dataframe)
|
return self.strategy.advise_buy(dataframe=dataframe, pair=pair)
|
||||||
|
|
||||||
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
|
def populate_sell_trend(self, dataframe: DataFrame, pair: str = None) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Based on TA indicators, populates the sell signal for the given dataframe
|
Based on TA indicators, populates the sell signal for the given dataframe
|
||||||
:param dataframe: DataFrame
|
:param dataframe: DataFrame
|
||||||
:return: DataFrame with buy column
|
:return: DataFrame with buy column
|
||||||
"""
|
"""
|
||||||
return self.strategy.populate_sell_trend(dataframe=dataframe)
|
return self.strategy.advise_sell(dataframe=dataframe, pair=pair)
|
||||||
|
|
||||||
def get_ticker_interval(self) -> str:
|
def get_ticker_interval(self) -> str:
|
||||||
"""
|
"""
|
||||||
@ -98,16 +98,17 @@ class Analyze(object):
|
|||||||
"""
|
"""
|
||||||
return self.strategy.ticker_interval
|
return self.strategy.ticker_interval
|
||||||
|
|
||||||
def analyze_ticker(self, ticker_history: List[Dict]) -> DataFrame:
|
def analyze_ticker(self, ticker_history: List[Dict], pair: str) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Parses the given ticker history and returns a populated DataFrame
|
Parses the given ticker history and returns a populated DataFrame
|
||||||
add several TA indicators and buy signal to it
|
add several TA indicators and buy signal to it
|
||||||
:return DataFrame with ticker data and indicator data
|
:return DataFrame with ticker data and indicator data
|
||||||
"""
|
"""
|
||||||
|
|
||||||
dataframe = self.parse_ticker_dataframe(ticker_history)
|
dataframe = self.parse_ticker_dataframe(ticker_history)
|
||||||
dataframe = self.populate_indicators(dataframe)
|
dataframe = self.populate_indicators(dataframe, pair)
|
||||||
dataframe = self.populate_buy_trend(dataframe)
|
dataframe = self.populate_buy_trend(dataframe, pair)
|
||||||
dataframe = self.populate_sell_trend(dataframe)
|
dataframe = self.populate_sell_trend(dataframe, pair)
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def get_signal(self, pair: str, interval: str) -> Tuple[bool, bool]:
|
def get_signal(self, pair: str, interval: str) -> Tuple[bool, bool]:
|
||||||
@ -123,7 +124,7 @@ class Analyze(object):
|
|||||||
return False, False
|
return False, False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dataframe = self.analyze_ticker(ticker_hist)
|
dataframe = self.analyze_ticker(ticker_hist, pair)
|
||||||
except ValueError as error:
|
except ValueError as error:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'Unable to analyze ticker for pair %s: %s',
|
'Unable to analyze ticker for pair %s: %s',
|
||||||
|
@ -156,7 +156,7 @@ class FreqtradeBot(object):
|
|||||||
state_changed |= self.process_maybe_execute_sell(trade)
|
state_changed |= self.process_maybe_execute_sell(trade)
|
||||||
|
|
||||||
# Then looking for buy opportunities
|
# Then looking for buy opportunities
|
||||||
if (self.config['disable_buy']):
|
if (self.config.get('disable_buy', False)):
|
||||||
logger.info('Buy disabled...')
|
logger.info('Buy disabled...')
|
||||||
else:
|
else:
|
||||||
if len(trades) < self.config['max_open_trades']:
|
if len(trades) < self.config['max_open_trades']:
|
||||||
@ -255,7 +255,7 @@ class FreqtradeBot(object):
|
|||||||
|
|
||||||
used_rate = ticker_rate
|
used_rate = ticker_rate
|
||||||
|
|
||||||
if self.config['bid_strategy']['use_book_order']:
|
if self.config['bid_strategy'].get('use_book_order', False):
|
||||||
logger.info('Getting price from Order Book')
|
logger.info('Getting price from Order Book')
|
||||||
orderBook = exchange.get_order_book(pair, self.config['bid_strategy']['book_order_top'])
|
orderBook = exchange.get_order_book(pair, self.config['bid_strategy']['book_order_top'])
|
||||||
orderBook_rate = orderBook['bids'][self.config['bid_strategy']['book_order_top']][0]
|
orderBook_rate = orderBook['bids'][self.config['bid_strategy']['book_order_top']][0]
|
||||||
@ -467,7 +467,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
sell_rate = self.analyze.get_roi_rate(trade)
|
sell_rate = self.analyze.get_roi_rate(trade)
|
||||||
logger.info('trying to selling at roi rate %0.8f', sell_rate)
|
logger.info('trying to selling at roi rate %0.8f', sell_rate)
|
||||||
|
|
||||||
if self.config['ask_strategy']['use_book_order'] and not is_set_fullfilled_at_roi:
|
if 'ask_strategy' in self.config and self.config['ask_strategy'].get('use_book_order', False):
|
||||||
logger.info('Using order book for selling...')
|
logger.info('Using order book for selling...')
|
||||||
|
|
||||||
# logger.debug('Order book %s',orderBook)
|
# logger.debug('Order book %s',orderBook)
|
||||||
@ -478,6 +478,7 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||||||
|
|
||||||
for i in range(orderBook_min, orderBook_max+1):
|
for i in range(orderBook_min, orderBook_max+1):
|
||||||
orderBook_rate = orderBook['asks'][i-1][0]
|
orderBook_rate = orderBook['asks'][i-1][0]
|
||||||
|
|
||||||
# if orderbook has higher rate (high profit),
|
# if orderbook has higher rate (high profit),
|
||||||
# use orderbook, otherwise just use bids rate
|
# use orderbook, otherwise just use bids rate
|
||||||
logger.info(' order book asks top %s: %0.8f', i, orderBook_rate)
|
logger.info(' order book asks top %s: %0.8f', i, orderBook_rate)
|
||||||
|
@ -71,7 +71,6 @@ def file_dump_json(filename, data, is_zip=False) -> None:
|
|||||||
:param data: JSON Data to save
|
:param data: JSON Data to save
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
print(f'dumping json to "{filename}"')
|
|
||||||
|
|
||||||
if is_zip:
|
if is_zip:
|
||||||
if not filename.endswith('.gz'):
|
if not filename.endswith('.gz'):
|
||||||
|
@ -79,7 +79,7 @@ class Backtesting(object):
|
|||||||
'total profit ' + stake_currency, 'avg duration', 'profit', 'loss']
|
'total profit ' + stake_currency, 'avg duration', 'profit', 'loss']
|
||||||
for pair in data:
|
for pair in data:
|
||||||
result = results[results.currency == pair]
|
result = results[results.currency == pair]
|
||||||
print(results)
|
|
||||||
tabular_data.append([
|
tabular_data.append([
|
||||||
pair,
|
pair,
|
||||||
len(result.index),
|
len(result.index),
|
||||||
@ -217,7 +217,6 @@ class Backtesting(object):
|
|||||||
if record and record.find('trades') >= 0:
|
if record and record.find('trades') >= 0:
|
||||||
logger.info('Dumping backtest results to %s', recordfilename)
|
logger.info('Dumping backtest results to %s', recordfilename)
|
||||||
file_dump_json(recordfilename, records)
|
file_dump_json(recordfilename, records)
|
||||||
file_dump_json('backtest-result.json', records)
|
|
||||||
labels = ['currency', 'profit_percent', 'profit_BTC', 'duration', 'entry', 'exit']
|
labels = ['currency', 'profit_percent', 'profit_BTC', 'duration', 'entry', 'exit']
|
||||||
|
|
||||||
return DataFrame.from_records(trades, columns=labels)
|
return DataFrame.from_records(trades, columns=labels)
|
||||||
@ -298,7 +297,7 @@ class Backtesting(object):
|
|||||||
|
|
||||||
# return date for data storage
|
# return date for data storage
|
||||||
table = self.aggregate(data, results)
|
table = self.aggregate(data, results)
|
||||||
return (results, table)
|
return results, table
|
||||||
|
|
||||||
|
|
||||||
def setup_configuration(args: Namespace) -> Dict[str, Any]:
|
def setup_configuration(args: Namespace) -> Dict[str, Any]:
|
||||||
|
@ -222,9 +222,7 @@ class Hyperopt(Backtesting):
|
|||||||
results['result'],
|
results['result'],
|
||||||
results['loss']
|
results['loss']
|
||||||
)
|
)
|
||||||
print(log_msg)
|
|
||||||
else:
|
else:
|
||||||
print('.', end='')
|
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
def calculate_loss(self, total_profit: float, trade_count: int, trade_duration: float) -> float:
|
def calculate_loss(self, total_profit: float, trade_count: int, trade_duration: float) -> float:
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
IStrategy interface
|
IStrategy interface
|
||||||
This module defines the interface to apply for strategies
|
This module defines the interface to apply for strategies
|
||||||
"""
|
"""
|
||||||
|
import warnings
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
|
from abc import ABC
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
|
|
||||||
class IStrategy(ABC):
|
class IStrategy(ABC):
|
||||||
"""
|
"""
|
||||||
Interface for freqtrade strategies
|
Interface for freqtrade strategies
|
||||||
@ -19,30 +19,71 @@ class IStrategy(ABC):
|
|||||||
ticker_interval -> str: value of the ticker interval to use for the strategy
|
ticker_interval -> str: value of the ticker interval to use for the strategy
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# associated minimal roi
|
||||||
minimal_roi: Dict
|
minimal_roi: Dict
|
||||||
|
|
||||||
|
# associated stoploss
|
||||||
stoploss: float
|
stoploss: float
|
||||||
|
|
||||||
|
# associated ticker interval
|
||||||
ticker_interval: str
|
ticker_interval: str
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Populate indicators that will be used in the Buy and Sell strategy
|
Populate indicators that will be used in the Buy and Sell strategy
|
||||||
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
|
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
|
||||||
:return: a Dataframe with all mandatory indicators for the strategies
|
:return: a Dataframe with all mandatory indicators for the strategies
|
||||||
"""
|
"""
|
||||||
|
warnings.warn("deprecated - please replace this method with advise_indicators!", DeprecationWarning)
|
||||||
|
return dataframe
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
|
def populate_buy_trend(self, dataframe: DataFrame) -> 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
|
||||||
:param dataframe: DataFrame
|
:param dataframe: DataFrame
|
||||||
:return: DataFrame with buy column
|
:return: DataFrame with buy column
|
||||||
"""
|
"""
|
||||||
|
warnings.warn("deprecated - please replace this method with advise_buy!", DeprecationWarning)
|
||||||
|
dataframe.loc[(), 'buy'] = 0
|
||||||
|
return dataframe
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
|
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Based on TA indicators, populates the sell signal for the given dataframe
|
Based on TA indicators, populates the sell signal for the given dataframe
|
||||||
:param dataframe: DataFrame
|
:param dataframe: DataFrame
|
||||||
:return: DataFrame with sell column
|
:return: DataFrame with sell column
|
||||||
"""
|
"""
|
||||||
|
warnings.warn("deprecated - please replace this method with advise_sell!", DeprecationWarning)
|
||||||
|
dataframe.loc[(), 'sell'] = 0
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def advise_indicators(self, dataframe: DataFrame, pair: str) -> DataFrame:
|
||||||
|
"""
|
||||||
|
|
||||||
|
This wraps around the internal method
|
||||||
|
|
||||||
|
Populate indicators that will be used in the Buy and Sell strategy
|
||||||
|
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
|
||||||
|
:param pair: The currently traded pair
|
||||||
|
:return: a Dataframe with all mandatory indicators for the strategies
|
||||||
|
"""
|
||||||
|
return self.populate_indicators(dataframe)
|
||||||
|
|
||||||
|
def advise_buy(self, dataframe: DataFrame, pair: str) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Based on TA indicators, populates the buy signal for the given dataframe
|
||||||
|
:param dataframe: DataFrame
|
||||||
|
:param pair: The currently traded pair
|
||||||
|
:return: DataFrame with buy column
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.populate_buy_trend(dataframe)
|
||||||
|
|
||||||
|
def advise_sell(self, dataframe: DataFrame, pair: str) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Based on TA indicators, populates the sell signal for the given dataframe
|
||||||
|
:param dataframe: DataFrame
|
||||||
|
:param pair: The currently traded pair
|
||||||
|
:return: DataFrame with sell column
|
||||||
|
"""
|
||||||
|
return self.populate_sell_trend(dataframe)
|
||||||
|
@ -40,6 +40,7 @@ class StrategyResolver(object):
|
|||||||
self.strategy: IStrategy = self._load_strategy(strategy_name,
|
self.strategy: IStrategy = self._load_strategy(strategy_name,
|
||||||
extra_dir=config.get('strategy_path'))
|
extra_dir=config.get('strategy_path'))
|
||||||
|
|
||||||
|
|
||||||
# Set attributes
|
# Set attributes
|
||||||
# Check if we need to override configuration
|
# Check if we need to override configuration
|
||||||
if 'minimal_roi' in config:
|
if 'minimal_roi' in config:
|
||||||
|
@ -520,7 +520,6 @@ def test_get_order(default_conf, mocker):
|
|||||||
order = MagicMock()
|
order = MagicMock()
|
||||||
order.myid = 123
|
order.myid = 123
|
||||||
exchange._DRY_RUN_OPEN_ORDERS['X'] = order
|
exchange._DRY_RUN_OPEN_ORDERS['X'] = order
|
||||||
print(exchange.get_order('X', 'TKN/BTC'))
|
|
||||||
assert exchange.get_order('X', 'TKN/BTC').myid == 123
|
assert exchange.get_order('X', 'TKN/BTC').myid == 123
|
||||||
|
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
|
@ -9,6 +9,7 @@ from unittest.mock import MagicMock
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
import pytest
|
||||||
from arrow import Arrow
|
from arrow import Arrow
|
||||||
|
|
||||||
from freqtrade import optimize
|
from freqtrade import optimize
|
||||||
@ -373,6 +374,7 @@ def test_generate_text_table(default_conf, mocker):
|
|||||||
assert backtesting._generate_text_table(data={'ETH/BTC': {}}, results=results) == result_str
|
assert backtesting._generate_text_table(data={'ETH/BTC': {}}, results=results) == result_str
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="no way of currently testing this")
|
||||||
def test_backtesting_start(default_conf, mocker, caplog) -> None:
|
def test_backtesting_start(default_conf, mocker, caplog) -> None:
|
||||||
"""
|
"""
|
||||||
Test Backtesting.start() method
|
Test Backtesting.start() method
|
||||||
@ -594,7 +596,6 @@ def test_backtest_record(default_conf, fee, mocker):
|
|||||||
results = backtesting.backtest(backtest_conf)
|
results = backtesting.backtest(backtest_conf)
|
||||||
assert len(results) == 3
|
assert len(results) == 3
|
||||||
# Assert file_dump_json was only called once
|
# Assert file_dump_json was only called once
|
||||||
print(names)
|
|
||||||
assert names == ['backtest-result.json']
|
assert names == ['backtest-result.json']
|
||||||
records = records[0]
|
records = records[0]
|
||||||
# Ensure records are of correct type
|
# Ensure records are of correct type
|
||||||
@ -615,6 +616,7 @@ def test_backtest_record(default_conf, fee, mocker):
|
|||||||
assert dur > 0
|
assert dur > 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="no way of currently testing this")
|
||||||
def test_backtest_start_live(default_conf, mocker, caplog):
|
def test_backtest_start_live(default_conf, mocker, caplog):
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
|
conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
|
||||||
|
@ -63,7 +63,6 @@ def test_scripts_options() -> None:
|
|||||||
arguments = Arguments(['-p', 'ETH/BTC'], '')
|
arguments = Arguments(['-p', 'ETH/BTC'], '')
|
||||||
arguments.scripts_options()
|
arguments.scripts_options()
|
||||||
args = arguments.get_parsed_arg()
|
args = arguments.get_parsed_arg()
|
||||||
print(args.pair)
|
|
||||||
assert args.pair == 'ETH/BTC'
|
assert args.pair == 'ETH/BTC'
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ def load_dataframe_pair(pairs):
|
|||||||
dataframe = ld[pairs[0]]
|
dataframe = ld[pairs[0]]
|
||||||
|
|
||||||
analyze = Analyze({'strategy': 'DefaultStrategy'})
|
analyze = Analyze({'strategy': 'DefaultStrategy'})
|
||||||
dataframe = analyze.analyze_ticker(dataframe)
|
dataframe = analyze.analyze_ticker(dataframe, pairs[0])
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ pytest-cov==2.5.1
|
|||||||
hyperopt==0.1
|
hyperopt==0.1
|
||||||
# do not upgrade networkx before this is fixed https://github.com/hyperopt/hyperopt/issues/325
|
# do not upgrade networkx before this is fixed https://github.com/hyperopt/hyperopt/issues/325
|
||||||
networkx==1.11
|
networkx==1.11
|
||||||
#git+git://github.com/berlinguyinca/networkx@v1.11
|
|
||||||
git+git://github.com/berlinguyinca/technical
|
git+git://github.com/berlinguyinca/technical
|
||||||
tabulate==0.8.2
|
tabulate==0.8.2
|
||||||
coinmarketcap==5.0.3
|
coinmarketcap==5.0.3
|
||||||
|
Loading…
Reference in New Issue
Block a user