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