- An helper strategy that runs signals evaluation concurrently

This commit is contained in:
orehunt 2020-03-24 16:33:33 +01:00
parent 030c487d6b
commit e697b5612a
2 changed files with 109 additions and 10 deletions

View File

@ -27,6 +27,7 @@ from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.state import State
from freqtrade.strategy.interface import IStrategy, SellType
from freqtrade.wallets import Wallets
from freqtrade.strategy.helper import get_all_signals
logger = logging.getLogger(__name__)
@ -216,9 +217,15 @@ class FreqtradeBot:
"but checking to sell open trades.")
else:
# Create entity and execute trade for each pair from whitelist
all_signals = get_all_signals(
self.strategy.get_signal, {
pair: (pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(pair, self.strategy.ticker_interval))
for pair in whitelist
})
for pair in whitelist:
try:
trades_created += self.create_trade(pair)
trades_created += self.create_trade(pair, all_signals[pair])
except DependencyException as exception:
logger.warning('Unable to create trade for %s: %s', pair, exception)
@ -379,7 +386,7 @@ class FreqtradeBot:
# See also #2575 at github.
return max(min_stake_amounts) / amount_reserve_percent
def create_trade(self, pair: str) -> bool:
def create_trade(self, pair: str, signals: Tuple[bool, bool] = None) -> bool:
"""
Check the implemented trading strategy for buy signals.
@ -401,6 +408,10 @@ class FreqtradeBot:
return False
# running get_signal on historical data fetched
if signals:
buy = signals[0]
sell = signals[1]
else:
(buy, sell) = self.strategy.get_signal(
pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(pair, self.strategy.ticker_interval))
@ -598,6 +609,13 @@ class FreqtradeBot:
Tries to execute sell orders for open trades (positions)
"""
trades_closed = 0
trades_pairlist = [t.pair for t in trades]
trades_signals = get_all_signals(
self.strategy.get_signal, {
pair: (pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(pair, self.strategy.ticker_interval))
for pair in trades_pairlist
})
for trade in trades:
try:
self.update_trade_state(trade)
@ -607,7 +625,8 @@ class FreqtradeBot:
trades_closed += 1
continue
# Check if we can sell our current pair
if trade.open_order_id is None and self.handle_trade(trade):
if trade.open_order_id is None and self.handle_trade(trade,
trades_signals[trade.pair]):
trades_closed += 1
except DependencyException as exception:
@ -658,7 +677,7 @@ class FreqtradeBot:
self._sell_rate_cache[pair] = rate
return rate
def handle_trade(self, trade: Trade) -> bool:
def handle_trade(self, trade: Trade, signals: Tuple[bool, bool] = None) -> bool:
"""
Sells the current pair if the threshold is reached and updates the trade record.
:return: True if trade has been sold, False otherwise
@ -674,6 +693,9 @@ class FreqtradeBot:
if (config_ask_strategy.get('use_sell_signal', True) or
config_ask_strategy.get('ignore_roi_if_buy_signal', False)):
if signals:
(buy, sell) = signals[0], signals[1]
else:
(buy, sell) = self.strategy.get_signal(
trade.pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(trade.pair, self.strategy.ticker_interval))

View File

@ -0,0 +1,77 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
import logging
import warnings
import numpy as np # noqa
from pandas import DataFrame
from typing import Dict, Callable
from functools import partial
from joblib import cpu_count, wrap_non_picklable_objects
from multiprocessing import Pool, Manager, Queue
from freqtrade.strategy.interface import IStrategy
# import talib.abstract as ta
# import freqtrade.vendor.qtpylib.indicators as qtpylib
logger = logging.getLogger(__name__)
def error_callback(e, q: Queue):
print(e)
q.put((None, None))
def get_all_signals(target: Callable, pairs_args: Dict, jobs=(cpu_count() // 2 or 1)) -> Dict:
""" Apply function over a dict where the values are the args of the function, parallelly """
results = {}
queue = Manager().Queue()
err = partial(error_callback, q=queue)
def func_queue(func: Callable, queue: Queue, pair: str, *args) -> DataFrame:
res = func(*args)
queue.put((pair, res))
return res
target = wrap_non_picklable_objects(target)
func_queue = wrap_non_picklable_objects(func_queue)
try:
with Pool(jobs) as p:
p.starmap_async(
func_queue,
[(target, queue, pair, *v) for pair, v in pairs_args.items()],
error_callback=err,
)
for pair in pairs_args:
proc_pair, res = queue.get()
if proc_pair:
results[proc_pair] = res
else:
break
# preserve the dict order
return {pair: results[pair] for pair in pairs_args}
except KeyError:
return {pair: target(*args) for pair, args in pairs_args.items()}
class HelperStrategy(IStrategy):
"""
This is a strategy template to get you started.
"""
time_weighted_roi = False
def __init__(self, config: dict) -> None:
super().__init__(config)
def ohlcvdata_to_dataframe(self, tickerdata: Dict[str, DataFrame]) -> Dict[str, DataFrame]:
"""
Creates a dataframe and populates indicators for given ticker data
Used by optimize operations only, not during dry / live runs.
"""
return get_all_signals(
self.advise_indicators,
{pair: (pair_data, {"pair": pair}) for pair, pair_data in tickerdata.items()},
)