- An helper strategy that runs signals evaluation concurrently
This commit is contained in:
parent
030c487d6b
commit
e697b5612a
@ -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))
|
||||
|
77
freqtrade/strategy/helper.py
Normal file
77
freqtrade/strategy/helper.py
Normal 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()},
|
||||
)
|
Loading…
Reference in New Issue
Block a user