Merge pull request #2661 from freqtrade/wallet_dry

Introduce Dry-Run Wallet
This commit is contained in:
hroff-1902
2019-12-16 14:00:11 +03:00
committed by GitHub
15 changed files with 111 additions and 31 deletions

View File

@@ -18,7 +18,7 @@ REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']
ORDERTYPE_POSSIBILITIES = ['limit', 'market']
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'PrecisionFilter', 'PriceFilter']
DRY_RUN_WALLET = 999.9
DRY_RUN_WALLET = 1000
MATH_CLOSE_PREC = 1e-14 # Precision used for float comparisons
USERPATH_HYPEROPTS = 'hyperopts'
@@ -75,7 +75,7 @@ CONF_SCHEMA = {
},
'fiat_display_currency': {'type': 'string', 'enum': SUPPORTED_FIAT},
'dry_run': {'type': 'boolean'},
'dry_run_wallet': {'type': 'number'},
'dry_run_wallet': {'type': 'number', 'default': DRY_RUN_WALLET},
'process_only_new_candles': {'type': 'boolean'},
'minimal_roi': {
'type': 'object',
@@ -275,6 +275,7 @@ CONF_SCHEMA = {
'stake_currency',
'stake_amount',
'dry_run',
'dry_run_wallet',
'bid_strategy',
'unfilledtimeout',
'stoploss',

View File

@@ -18,7 +18,7 @@ from ccxt.base.decimal_to_precision import ROUND_DOWN, ROUND_UP
from pandas import DataFrame
from freqtrade import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError, constants)
OperationalException, TemporaryError)
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async
from freqtrade.misc import deep_merge_dicts
@@ -479,7 +479,7 @@ class Exchange:
@retrier
def get_balance(self, currency: str) -> float:
if self._config['dry_run']:
return constants.DRY_RUN_WALLET
return self._config['dry_run_wallet']
# ccxt exception is already handled by get_balances
balances = self.get_balances()

View File

@@ -62,7 +62,11 @@ class FreqtradeBot:
self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange
persistence.init(self.config.get('db_url', None),
clean_open_orders=self.config.get('dry_run', False))
self.wallets = Wallets(self.config, self.exchange)
self.dataprovider = DataProvider(self.config, self.exchange)
# Attach Dataprovider to Strategy baseclass
@@ -78,9 +82,6 @@ class FreqtradeBot:
self.active_pair_whitelist = self._refresh_whitelist()
persistence.init(self.config.get('db_url', None),
clean_open_orders=self.config.get('dry_run', False))
# Set initial bot state from config
initial_state = self.config.get('initial_state')
self.state = State[initial_state.upper()] if initial_state else State.STOPPED
@@ -231,8 +232,8 @@ class FreqtradeBot:
# Check if stake_amount is fulfilled
if available_amount < stake_amount:
raise DependencyException(
f"Available balance({available_amount} {self.config['stake_currency']}) is "
f"lower than stake amount({stake_amount} {self.config['stake_currency']})"
f"Available balance ({available_amount} {self.config['stake_currency']}) is "
f"lower than stake amount ({stake_amount} {self.config['stake_currency']})"
)
return stake_amount

View File

@@ -348,6 +348,7 @@ class RPC:
'total': total,
'symbol': symbol,
'value': value,
'note': 'Simulated balances' if self._freqtrade.config.get('dry_run', False) else ''
}
def _rpc_start(self) -> Dict[str, str]:

View File

@@ -331,7 +331,15 @@ class Telegram(RPC):
try:
result = self._rpc_balance(self._config['stake_currency'],
self._config.get('fiat_display_currency', ''))
output = ''
if self._config['dry_run']:
output += (
f"*Warning:*Simulated balances in Dry Mode.\n"
"This mode is still experimental!\n"
"Starting capital: "
f"`{self._config['dry_run_wallet']}` {self._config['stake_currency']}.\n"
)
for currency in result['currencies']:
if currency['est_stake'] > 0.0001:
curr_output = "*{currency}:*\n" \

View File

@@ -4,7 +4,7 @@
import logging
from typing import Dict, NamedTuple, Any
from freqtrade.exchange import Exchange
from freqtrade import constants
from freqtrade.persistence import Trade
logger = logging.getLogger(__name__)
@@ -23,14 +23,12 @@ class Wallets:
self._config = config
self._exchange = exchange
self._wallets: Dict[str, Wallet] = {}
self.start_cap = config['dry_run_wallet']
self.update()
def get_free(self, currency) -> float:
if self._config['dry_run']:
return self._config.get('dry_run_wallet', constants.DRY_RUN_WALLET)
balance = self._wallets.get(currency)
if balance and balance.free:
return balance.free
@@ -39,9 +37,6 @@ class Wallets:
def get_used(self, currency) -> float:
if self._config['dry_run']:
return self._config.get('dry_run_wallet', constants.DRY_RUN_WALLET)
balance = self._wallets.get(currency)
if balance and balance.used:
return balance.used
@@ -50,16 +45,42 @@ class Wallets:
def get_total(self, currency) -> float:
if self._config['dry_run']:
return self._config.get('dry_run_wallet', constants.DRY_RUN_WALLET)
balance = self._wallets.get(currency)
if balance and balance.total:
return balance.total
else:
return 0
def update(self) -> None:
def _update_dry(self) -> None:
"""
Update from database in dry-run mode
- Apply apply profits of closed trades on top of stake amount
- Subtract currently tied up stake_amount in open trades
- update balances for currencies currently in trades
"""
closed_trades = Trade.get_trades(Trade.is_open.is_(False)).all()
open_trades = Trade.get_trades(Trade.is_open.is_(True)).all()
tot_profit = sum([trade.calc_profit() for trade in closed_trades])
tot_in_trades = sum([trade.stake_amount for trade in open_trades])
current_stake = self.start_cap + tot_profit - tot_in_trades
self._wallets[self._config['stake_currency']] = Wallet(
self._config['stake_currency'],
current_stake,
0,
current_stake
)
for trade in open_trades:
curr = trade.pair.split('/')[0]
self._wallets[curr] = Wallet(
curr,
trade.amount,
0,
trade.amount
)
def _update_live(self) -> None:
balances = self._exchange.get_balances()
@@ -71,6 +92,11 @@ class Wallets:
balances[currency].get('total', None)
)
def update(self) -> None:
if self._config['dry_run']:
self._update_dry()
else:
self._update_live()
logger.info('Wallets synced.')
def get_all_balances(self) -> Dict[str, Any]: