Merge branch 'feat/short' into lev-freqtradebot

This commit is contained in:
Sam Germain
2021-10-13 19:02:57 -06:00
53 changed files with 902 additions and 313 deletions

View File

@@ -73,7 +73,7 @@ ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit",
ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
"trade_source", "timeframe", "plot_auto_open"]
ARGS_INSTALL_UI = ["erase_ui_only"]
ARGS_INSTALL_UI = ["erase_ui_only", 'ui_version']
ARGS_SHOW_TRADES = ["db_url", "trade_ids", "print_json"]

View File

@@ -163,7 +163,8 @@ def ask_user_config() -> Dict[str, Any]:
{
"type": "text",
"name": "api_server_listen_addr",
"message": "Insert Api server Listen Address (best left untouched default!)",
"message": ("Insert Api server Listen Address (0.0.0.0 for docker, "
"otherwise best left untouched)"),
"default": "127.0.0.1",
"when": lambda x: x['api_server']
},

View File

@@ -414,6 +414,12 @@ AVAILABLE_CLI_OPTIONS = {
action='store_true',
default=False,
),
"ui_version": Arg(
'--ui-version',
help=('Specify a specific version of FreqUI to install. '
'Not specifying this installs the latest version.'),
type=str,
),
# Templating options
"template": Arg(
'--template',

View File

@@ -128,7 +128,7 @@ def download_and_install_ui(dest_folder: Path, dl_url: str, version: str):
f.write(version)
def get_ui_download_url() -> Tuple[str, str]:
def get_ui_download_url(version: Optional[str] = None) -> Tuple[str, str]:
base_url = 'https://api.github.com/repos/freqtrade/frequi/'
# Get base UI Repo path
@@ -136,8 +136,16 @@ def get_ui_download_url() -> Tuple[str, str]:
resp.raise_for_status()
r = resp.json()
latest_version = r[0]['name']
assets = r[0].get('assets', [])
if version:
tmp = [x for x in r if x['name'] == version]
if tmp:
latest_version = tmp[0]['name']
assets = tmp[0].get('assets', [])
else:
raise ValueError("UI-Version not found.")
else:
latest_version = r[0]['name']
assets = r[0].get('assets', [])
dl_url = ''
if assets and len(assets) > 0:
dl_url = assets[0]['browser_download_url']
@@ -156,7 +164,7 @@ def start_install_ui(args: Dict[str, Any]) -> None:
dest_folder = Path(__file__).parents[1] / 'rpc/api_server/ui/installed/'
# First make sure the assets are removed.
dl_url, latest_version = get_ui_download_url()
dl_url, latest_version = get_ui_download_url(args.get('ui_version'))
curr_version = read_ui_version(dest_folder)
if curr_version == latest_version and not args.get('erase_ui_only'):

View File

@@ -24,7 +24,8 @@ ORDERTYPE_POSSIBILITIES = ['limit', 'market']
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss',
'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily',
'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily']
'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily',
'MaxDrawDownHyperOptLoss']
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
'AgeFilter', 'OffsetFilter', 'PerformanceFilter',
'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter',

View File

@@ -1,6 +1,6 @@
""" Bibox exchange subclass """
import logging
from typing import Dict
from typing import Dict, List
from freqtrade.exchange import Exchange
@@ -24,3 +24,5 @@ class Bibox(Exchange):
def _ccxt_config(self) -> Dict:
# Parameters to add directly to ccxt sync/async initialization.
return {"has": {"fetchCurrencies": False}}
funding_fee_times: List[int] = [0, 8, 16] # hours of the day

View File

@@ -28,6 +28,8 @@ class Binance(Exchange):
"trades_pagination_arg": "fromId",
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
}
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
# but the schedule won't check within this timeframe
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
# TradingMode.SPOT always supported and not required in this list

View File

@@ -1,7 +1,8 @@
""" Bybit exchange subclass """
import logging
from typing import Dict
from typing import Dict, List, Tuple
from freqtrade.enums import Collateral, TradingMode
from freqtrade.exchange import Exchange
@@ -21,3 +22,11 @@ class Bybit(Exchange):
_ft_has: Dict = {
"ohlcv_candle_limit": 200,
}
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
# TradingMode.SPOT always supported and not required in this list
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
]

View File

@@ -9,7 +9,7 @@ import logging
from copy import deepcopy
from datetime import datetime, timezone
from math import ceil
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional, Tuple, Union
import arrow
import ccxt
@@ -72,6 +72,10 @@ class Exchange:
}
_ft_has: Dict = {}
# funding_fee_times is currently unused, but should ideally be used to properly
# schedule refresh times
funding_fee_times: List[int] = [] # hours of the day
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
# TradingMode.SPOT always supported and not required in this list
]
@@ -503,7 +507,7 @@ class Exchange:
if startup_candles + 5 > candle_limit:
raise OperationalException(
f"This strategy requires {startup_candles} candles to start. "
f"{self.name} only provides {candle_limit} for {timeframe}.")
f"{self.name} only provides {candle_limit - 5} for {timeframe}.")
def validate_trading_mode_and_collateral(
self,
@@ -565,7 +569,7 @@ class Exchange:
precision = self.markets[pair]['precision']['price']
missing = price % precision
if missing != 0:
price = price - missing + precision
price = round(price - missing + precision, 10)
else:
symbol_prec = self.markets[pair]['precision']['price']
big_price = price * pow(10, symbol_prec)
@@ -1130,7 +1134,7 @@ class Exchange:
ticker_rate = ticker[conf_strategy['price_side']]
if ticker['last'] and ticker_rate:
if side == 'buy' and ticker_rate > ticker['last']:
balance = conf_strategy['ask_last_balance']
balance = conf_strategy.get('ask_last_balance', 0.0)
ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate)
elif side == 'sell' and ticker_rate < ticker['last']:
balance = conf_strategy.get('bid_last_balance', 0.0)
@@ -1600,6 +1604,37 @@ class Exchange:
self._async_get_trade_history(pair=pair, since=since,
until=until, from_id=from_id))
@retrier
def get_funding_fees_from_exchange(self, pair: str, since: Union[datetime, int]) -> float:
"""
Returns the sum of all funding fees that were exchanged for a pair within a timeframe
:param pair: (e.g. ADA/USDT)
:param since: The earliest time of consideration for calculating funding fees,
in unix time or as a datetime
"""
# TODO-lev: Add dry-run handling for this.
if not self.exchange_has("fetchFundingHistory"):
raise OperationalException(
f"fetch_funding_history() has not been implemented on ccxt.{self.name}")
if type(since) is datetime:
since = int(since.timestamp()) * 1000 # * 1000 for ms
try:
funding_history = self._api.fetch_funding_history(
pair=pair,
since=since
)
return sum(fee['amount'] for fee in funding_history)
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not get funding fees due to {e.__class__.__name__}. Message: {e}') from e
except ccxt.BaseError as e:
raise OperationalException(e) from e
def fill_leverage_brackets(self):
"""
# TODO-lev: Should maybe be renamed, leverage_brackets might not be accurate for kraken

View File

@@ -21,6 +21,7 @@ class Ftx(Exchange):
"stoploss_on_exchange": True,
"ohlcv_candle_limit": 1500,
}
funding_fee_times: List[int] = list(range(0, 24))
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
# TradingMode.SPOT always supported and not required in this list

View File

@@ -1,7 +1,8 @@
""" Gate.io exchange subclass """
import logging
from typing import Dict
from typing import Dict, List
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import Exchange
@@ -23,3 +24,12 @@ class Gateio(Exchange):
}
_headers = {'X-Gate-Channel-Id': 'freqtrade'}
funding_fee_times: List[int] = [0, 8, 16] # hours of the day
def validate_ordertypes(self, order_types: Dict) -> None:
super().validate_ordertypes(order_types)
if any(v == 'market' for k, v in order_types.items()):
raise OperationalException(
f'Exchange {self.name} does not support market orders.')

View File

@@ -1,5 +1,5 @@
import logging
from typing import Dict
from typing import Dict, List
from freqtrade.exchange import Exchange
@@ -21,3 +21,5 @@ class Hitbtc(Exchange):
"ohlcv_candle_limit": 1000,
"ohlcv_params": {"sort": "DESC"}
}
funding_fee_times: List[int] = [0, 8, 16] # hours of the day

View File

@@ -23,6 +23,7 @@ class Kraken(Exchange):
"trades_pagination": "id",
"trades_pagination_arg": "since",
}
funding_fee_times: List[int] = [0, 4, 8, 12, 16, 20] # hours of the day
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
# TradingMode.SPOT always supported and not required in this list

View File

@@ -1,6 +1,6 @@
""" Kucoin exchange subclass """
import logging
from typing import Dict
from typing import Dict, List
from freqtrade.exchange import Exchange
@@ -24,3 +24,5 @@ class Kucoin(Exchange):
"order_time_in_force": ['gtc', 'fok', 'ioc'],
"time_in_force_parameter": "timeInForce",
}
funding_fee_times: List[int] = [4, 12, 20] # hours of the day

View File

@@ -4,12 +4,13 @@ Freqtrade is the main module of this bot. It contains the class Freqtrade()
import copy
import logging
import traceback
from datetime import datetime, timezone
from datetime import datetime, time, timezone
from math import isclose
from threading import Lock
from typing import Any, Dict, List, Optional, Tuple
import arrow
from schedule import Scheduler
from freqtrade import __version__, constants
from freqtrade.configuration import validate_config_consistency
@@ -108,14 +109,26 @@ class FreqtradeBot(LoggingMixin):
self.trading_mode: TradingMode = TradingMode.SPOT
self.collateral_type: Optional[Collateral] = None
trading_mode = self.config.get('trading_mode')
collateral_type = self.config.get('collateral_type')
if 'trading_mode' in self.config:
self.trading_mode = TradingMode(self.config['trading_mode'])
if trading_mode:
self.trading_mode = TradingMode(trading_mode)
if 'collateral_type' in self.config:
self.collateral_type = Collateral(self.config['collateral_type'])
if collateral_type:
self.collateral_type = Collateral(collateral_type)
self._schedule = Scheduler()
if self.trading_mode == TradingMode.FUTURES:
def update():
self.update_funding_fees()
self.wallets.update()
# TODO: This would be more efficient if scheduled in utc time, and performed at each
# TODO: funding interval, specified by funding_fee_times on the exchange classes
for time_slot in range(0, 24):
for minutes in [0, 15, 30, 45]:
t = str(time(time_slot, minutes, 2))
self._schedule.every().day.at(t).do(update)
def notify_status(self, msg: str) -> None:
"""
@@ -196,7 +209,8 @@ class FreqtradeBot(LoggingMixin):
# Then looking for buy opportunities
if self.get_free_open_trades():
self.enter_positions()
if self.trading_mode == TradingMode.FUTURES:
self._schedule.run_pending()
Trade.commit()
def process_stopped(self) -> None:
@@ -252,6 +266,15 @@ class FreqtradeBot(LoggingMixin):
open_trades = len(Trade.get_open_trades())
return max(0, self.config['max_open_trades'] - open_trades)
def update_funding_fees(self):
if self.trading_mode == TradingMode.FUTURES:
for trade in Trade.get_open_trades():
funding_fees = self.exchange.get_funding_fees_from_exchange(
trade.pair,
trade.open_date
)
trade.funding_fees = funding_fees
def startup_update_open_orders(self):
"""
Updates open orders based on order list kept in the database.
@@ -274,6 +297,9 @@ class FreqtradeBot(LoggingMixin):
logger.warning(f"Error updating Order {order.order_id} due to {e}")
if self.trading_mode == TradingMode.FUTURES:
self._schedule.run_pending()
def update_closed_trades_without_assigned_fees(self):
"""
Update closed trades without close fees assigned.
@@ -679,6 +705,12 @@ class FreqtradeBot(LoggingMixin):
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
open_date = datetime.now(timezone.utc)
if self.trading_mode == TradingMode.FUTURES:
funding_fees = self.exchange.get_funding_fees_from_exchange(pair, open_date)
else:
funding_fees = 0.0
trade = Trade(
pair=pair,
stake_amount=stake_amount,
@@ -689,7 +721,7 @@ class FreqtradeBot(LoggingMixin):
fee_close=fee,
open_rate=enter_limit_filled_price,
open_rate_requested=enter_limit_requested,
open_date=datetime.utcnow(),
open_date=open_date,
exchange=self.exchange.id,
open_order_id=order_id,
strategy=self.strategy.get_strategy_name(),
@@ -700,6 +732,8 @@ class FreqtradeBot(LoggingMixin):
is_short=is_short,
interest_rate=interest_rate,
isolated_liq=isolated_liq,
trading_mode=self.trading_mode,
funding_fees=funding_fees
)
trade.orders.append(order_obj)

View File

@@ -0,0 +1,41 @@
"""
MaxDrawDownHyperOptLoss
This module defines the alternative HyperOptLoss class which can be used for
Hyperoptimization.
"""
from datetime import datetime
from pandas import DataFrame
from freqtrade.data.btanalysis import calculate_max_drawdown
from freqtrade.optimize.hyperopt import IHyperOptLoss
class MaxDrawDownHyperOptLoss(IHyperOptLoss):
"""
Defines the loss function for hyperopt.
This implementation optimizes for max draw down and profit
Less max drawdown more profit -> Lower return value
"""
@staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int,
min_date: datetime, max_date: datetime,
*args, **kwargs) -> float:
"""
Objective function.
Uses profit ratio weighted max_drawdown when drawdown is available.
Otherwise directly optimizes profit ratio.
"""
total_profit = results['profit_abs'].sum()
try:
max_drawdown = calculate_max_drawdown(results, value_col='profit_abs')
except ValueError:
# No losing trade, therefore no drawdown.
return -total_profit
return -total_profit / max_drawdown[0]

View File

@@ -49,11 +49,20 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
strategy = get_column_def(cols, 'strategy', 'null')
buy_tag = get_column_def(cols, 'buy_tag', 'null')
trading_mode = get_column_def(cols, 'trading_mode', 'null')
# Leverage Properties
leverage = get_column_def(cols, 'leverage', '1.0')
interest_rate = get_column_def(cols, 'interest_rate', '0.0')
isolated_liq = get_column_def(cols, 'isolated_liq', 'null')
# sqlite does not support literals for booleans
is_short = get_column_def(cols, 'is_short', '0')
# Margin Properties
interest_rate = get_column_def(cols, 'interest_rate', '0.0')
# Futures properties
funding_fees = get_column_def(cols, 'funding_fees', '0.0')
# If ticker-interval existed use that, else null.
if has_column(cols, 'ticker_interval'):
timeframe = get_column_def(cols, 'timeframe', 'ticker_interval')
@@ -91,7 +100,8 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
stoploss_order_id, stoploss_last_update,
max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_tag,
timeframe, open_trade_value, close_profit_abs,
leverage, interest_rate, isolated_liq, is_short
trading_mode, leverage, isolated_liq, is_short,
interest_rate, funding_fees
)
select id, lower(exchange), pair,
is_open, {fee_open} fee_open, {fee_open_cost} fee_open_cost,
@@ -108,8 +118,9 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
{sell_order_status} sell_order_status,
{strategy} strategy, {buy_tag} buy_tag, {timeframe} timeframe,
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs,
{leverage} leverage, {interest_rate} interest_rate,
{isolated_liq} isolated_liq, {is_short} is_short
{trading_mode} trading_mode, {leverage} leverage, {isolated_liq} isolated_liq,
{is_short} is_short, {interest_rate} interest_rate,
{funding_fees} funding_fees
from {table_back_name}
"""))
@@ -169,7 +180,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
table_back_name = get_backup_name(tabs, 'trades_bak')
# Check for latest column
if not has_column(cols, 'is_short'):
if not has_column(cols, 'funding_fees'):
logger.info(f'Running database migration for trades - backup: {table_back_name}')
migrate_trades_table(decl_base, inspector, engine, table_back_name, cols)
# Reread columns - the above recreated the table!

View File

@@ -6,7 +6,7 @@ from datetime import datetime, timedelta, timezone
from decimal import Decimal
from typing import Any, Dict, List, Optional
from sqlalchemy import (Boolean, Column, DateTime, Float, ForeignKey, Integer, String,
from sqlalchemy import (Boolean, Column, DateTime, Enum, Float, ForeignKey, Integer, String,
create_engine, desc, func, inspect)
from sqlalchemy.exc import NoSuchModuleError
from sqlalchemy.orm import Query, declarative_base, relationship, scoped_session, sessionmaker
@@ -14,7 +14,7 @@ from sqlalchemy.pool import StaticPool
from sqlalchemy.sql.schema import UniqueConstraint
from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES
from freqtrade.enums import SellType
from freqtrade.enums import SellType, TradingMode
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.leverage import interest
from freqtrade.misc import safe_value_fallback
@@ -265,14 +265,19 @@ class LocalTrade():
buy_tag: Optional[str] = None
timeframe: Optional[int] = None
trading_mode: TradingMode = TradingMode.SPOT
# Leverage trading properties
is_short: bool = False
isolated_liq: Optional[float] = None
is_short: bool = False
leverage: float = 1.0
# Margin trading properties
interest_rate: float = 0.0
# Futures properties
funding_fees: Optional[float] = None
@property
def has_no_leverage(self) -> bool:
"""Returns true if this is a non-leverage, non-short trade"""
@@ -439,7 +444,8 @@ class LocalTrade():
'interest_rate': self.interest_rate,
'isolated_liq': self.isolated_liq,
'is_short': self.is_short,
'trading_mode': self.trading_mode,
'funding_fees': self.funding_fees,
'open_order_id': self.open_order_id,
}
@@ -642,7 +648,7 @@ class LocalTrade():
zero = Decimal(0.0)
# If nothing was borrowed
if self.has_no_leverage:
if self.has_no_leverage or self.trading_mode != TradingMode.MARGIN:
return zero
open_date = self.open_date.replace(tzinfo=None)
@@ -656,6 +662,17 @@ class LocalTrade():
return interest(exchange_name=self.exchange, borrowed=borrowed, rate=rate, hours=hours)
def _calc_base_close(self, amount: Decimal, rate: Optional[float] = None,
fee: Optional[float] = None) -> Decimal:
close_trade = Decimal(amount) * Decimal(rate or self.close_rate) # type: ignore
fees = close_trade * Decimal(fee or self.fee_close)
if self.is_short:
return close_trade + fees
else:
return close_trade - fees
def calc_close_trade_value(self, rate: Optional[float] = None,
fee: Optional[float] = None,
interest_rate: Optional[float] = None) -> float:
@@ -672,20 +689,32 @@ class LocalTrade():
if rate is None and not self.close_rate:
return 0.0
interest = self.calculate_interest(interest_rate)
if self.is_short:
amount = Decimal(self.amount) + Decimal(interest)
else:
# Currency already owned for longs, no need to purchase
amount = Decimal(self.amount)
amount = Decimal(self.amount)
trading_mode = self.trading_mode or TradingMode.SPOT
close_trade = Decimal(amount) * Decimal(rate or self.close_rate) # type: ignore
fees = close_trade * Decimal(fee or self.fee_close)
if trading_mode == TradingMode.SPOT:
return float(self._calc_base_close(amount, rate, fee))
if self.is_short:
return float(close_trade + fees)
elif (trading_mode == TradingMode.MARGIN):
total_interest = self.calculate_interest(interest_rate)
if self.is_short:
amount = amount + total_interest
return float(self._calc_base_close(amount, rate, fee))
else:
# Currency already owned for longs, no need to purchase
return float(self._calc_base_close(amount, rate, fee) - total_interest)
elif (trading_mode == TradingMode.FUTURES):
funding_fees = self.funding_fees or 0.0
if self.is_short:
return float(self._calc_base_close(amount, rate, fee)) - funding_fees
else:
return float(self._calc_base_close(amount, rate, fee)) + funding_fees
else:
return float(close_trade - fees - interest)
raise OperationalException(
f"{self.trading_mode.value} trading is not yet available using freqtrade")
def calc_profit(self, rate: Optional[float] = None,
fee: Optional[float] = None,
@@ -893,6 +922,8 @@ class Trade(_DECL_BASE, LocalTrade):
buy_tag = Column(String(100), nullable=True)
timeframe = Column(Integer, nullable=True)
trading_mode = Column(Enum(TradingMode), nullable=True)
# Leverage trading properties
leverage = Column(Float, nullable=True, default=1.0)
is_short = Column(Boolean, nullable=False, default=False)
@@ -901,6 +932,9 @@ class Trade(_DECL_BASE, LocalTrade):
# Margin Trading Properties
interest_rate = Column(Float, nullable=False, default=0.0)
# Futures properties
funding_fees = Column(Float, nullable=True, default=None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.recalc_open_trade_value()

View File

@@ -347,3 +347,8 @@ class BacktestResponse(BaseModel):
trade_count: Optional[float]
# TODO: Properly type backtestresult...
backtest_result: Optional[Dict[str, Any]]
class SysInfo(BaseModel):
cpu_pct: List[float]
ram_pct: float

View File

@@ -18,7 +18,8 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, Blac
OpenTradeSchema, PairHistory, PerformanceEntry,
Ping, PlotConfig, Profit, ResultMsg, ShowConfig,
Stats, StatusMsg, StrategyListResponse,
StrategyResponse, Version, WhitelistResponse)
StrategyResponse, SysInfo, Version,
WhitelistResponse)
from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional
from freqtrade.rpc.rpc import RPCException
@@ -259,3 +260,8 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option
'pair_interval': pair_interval,
}
return result
@router.get('/sysinfo', response_model=SysInfo, tags=['info'])
def sysinfo():
return RPC._rpc_sysinfo()

View File

@@ -8,6 +8,7 @@ from math import isnan
from typing import Any, Dict, List, Optional, Tuple, Union
import arrow
import psutil
from numpy import NAN, inf, int64, mean
from pandas import DataFrame
@@ -871,3 +872,10 @@ class RPC:
'subplots' not in self._freqtrade.strategy.plot_config):
self._freqtrade.strategy.plot_config['subplots'] = {}
return self._freqtrade.strategy.plot_config
@staticmethod
def _rpc_sysinfo() -> Dict[str, Any]:
return {
"cpu_pct": psutil.cpu_percent(interval=1, percpu=True),
"ram_pct": psutil.virtual_memory().percent
}

View File

@@ -2,11 +2,8 @@
"name": "{{ exchange_name | lower }}",
"key": "{{ exchange_key }}",
"secret": "{{ exchange_secret }}",
"ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": {
"enableRateLimit": true,
"rateLimit": 200
},
"ccxt_config": {},
"ccxt_async_config": {},
"pair_whitelist": [
],
"pair_blacklist": [

View File

@@ -2,10 +2,8 @@
"name": "{{ exchange_name | lower }}",
"key": "{{ exchange_key }}",
"secret": "{{ exchange_secret }}",
"ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": {
"enableRateLimit": true
},
"ccxt_config": {},
"ccxt_async_config": {},
"pair_whitelist": [
],

View File

@@ -3,14 +3,8 @@
"key": "{{ exchange_key }}",
"secret": "{{ exchange_secret }}",
"password": "{{ exchange_key_password }}",
"ccxt_config": {
"enableRateLimit": true,
"rateLimit": 200
},
"ccxt_async_config": {
"enableRateLimit": true,
"rateLimit": 200
},
"ccxt_config": {},
"ccxt_async_config": {},
"pair_whitelist": [
],
"pair_blacklist": [

View File

@@ -32,8 +32,7 @@ def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: f
use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime',
current_rate: float, current_profit: float, dataframe: DataFrame,
**kwargs) -> float:
current_rate: float, current_profit: float, **kwargs) -> float:
"""
Custom stoploss logic, returning the new distance relative to current_rate (as ratio).
e.g. returning -0.05 would create a stoploss 5% below current_rate.
@@ -44,14 +43,13 @@ def custom_stoploss(self, pair: str, trade: 'Trade', current_time: 'datetime',
When not implemented by a strategy, returns the initial stoploss value
Only called when use_custom_stoploss is set to True.
:param pair: Pair that's about to be sold.
:param pair: Pair that's currently analyzed
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param dataframe: Analyzed dataframe for this pair. Can contain future data in backtesting.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New stoploss value, relative to the currentrate
:return float: New stoploss value, relative to the current_rate
"""
return self.stoploss