Merge branch 'margin-db' into margin-commands
This commit is contained in:
commit
ab796ccd33
@ -1,11 +1,20 @@
|
||||
{
|
||||
"name": "freqtrade Develop",
|
||||
|
||||
"dockerComposeFile": [
|
||||
"docker-compose.yml"
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": ".."
|
||||
},
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
"forwardPorts": [
|
||||
8080
|
||||
],
|
||||
"mounts": [
|
||||
"source=freqtrade-bashhistory,target=/home/ftuser/commandhistory,type=volume"
|
||||
],
|
||||
// Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "ftuser",
|
||||
|
||||
"service": "ft_vscode",
|
||||
"postCreateCommand": "freqtrade create-userdir --userdir user_data/",
|
||||
|
||||
"workspaceFolder": "/freqtrade/",
|
||||
|
||||
@ -25,20 +34,6 @@
|
||||
"ms-python.vscode-pylance",
|
||||
"davidanson.vscode-markdownlint",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"vscode-icons-team.vscode-icons",
|
||||
],
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Uncomment the next line if you want start specific services in your Docker Compose config.
|
||||
// "runServices": [],
|
||||
|
||||
// Uncomment the next line if you want to keep your containers running after VS Code shuts down.
|
||||
// "shutdownAction": "none",
|
||||
|
||||
// Uncomment the next line to run commands after the container is created - for example installing curl.
|
||||
// "postCreateCommand": "sudo apt-get update && apt-get install -y git",
|
||||
|
||||
// Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "ftuser"
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
---
|
||||
version: '3'
|
||||
services:
|
||||
ft_vscode:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: ".devcontainer/Dockerfile"
|
||||
volumes:
|
||||
# Allow git usage within container
|
||||
- "${HOME}/.ssh:/home/ftuser/.ssh:ro"
|
||||
- "${HOME}/.gitconfig:/home/ftuser/.gitconfig:ro"
|
||||
- ..:/freqtrade:cached
|
||||
# Persist bash-history
|
||||
- freqtrade-vscode-server:/home/ftuser/.vscode-server
|
||||
- freqtrade-bashhistory:/home/ftuser/commandhistory
|
||||
# Expose API port
|
||||
ports:
|
||||
- "127.0.0.1:8080:8080"
|
||||
command: /bin/sh -c "while sleep 1000; do :; done"
|
||||
|
||||
|
||||
volumes:
|
||||
freqtrade-vscode-server:
|
||||
freqtrade-bashhistory:
|
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -79,13 +79,13 @@ jobs:
|
||||
|
||||
- name: Backtesting
|
||||
run: |
|
||||
cp config_bittrex.json.example config.json
|
||||
cp config_examples/config_bittrex.example.json config.json
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy
|
||||
|
||||
- name: Hyperopt
|
||||
run: |
|
||||
cp config_bittrex.json.example config.json
|
||||
cp config_examples/config_bittrex.example.json config.json
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all
|
||||
|
||||
@ -172,13 +172,13 @@ jobs:
|
||||
|
||||
- name: Backtesting
|
||||
run: |
|
||||
cp config_bittrex.json.example config.json
|
||||
cp config_examples/config_bittrex.example.json config.json
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy
|
||||
|
||||
- name: Hyperopt
|
||||
run: |
|
||||
cp config_bittrex.json.example config.json
|
||||
cp config_examples/config_bittrex.example.json config.json
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all
|
||||
|
||||
@ -239,13 +239,13 @@ jobs:
|
||||
|
||||
- name: Backtesting
|
||||
run: |
|
||||
cp config_bittrex.json.example config.json
|
||||
cp config_examples/config_bittrex.example.json config.json
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy
|
||||
|
||||
- name: Hyperopt
|
||||
run: |
|
||||
cp config_bittrex.json.example config.json
|
||||
cp config_examples/config_bittrex.example.json config.json
|
||||
freqtrade create-userdir --userdir user_data
|
||||
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all
|
||||
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -95,3 +95,8 @@ target/
|
||||
|
||||
#exceptions
|
||||
!*.gitkeep
|
||||
!config_examples/config_binance.example.json
|
||||
!config_examples/config_bittrex.example.json
|
||||
!config_examples/config_ftx.example.json
|
||||
!config_examples/config_full.example.json
|
||||
!config_examples/config_kraken.example.json
|
||||
|
@ -26,12 +26,12 @@ jobs:
|
||||
# - coveralls || true
|
||||
name: pytest
|
||||
- script:
|
||||
- cp config_bittrex.json.example config.json
|
||||
- cp config_examples/config_bittrex.example.json config.json
|
||||
- freqtrade create-userdir --userdir user_data
|
||||
- freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy
|
||||
name: backtest
|
||||
- script:
|
||||
- cp config_bittrex.json.example config.json
|
||||
- cp config_examples/config_bittrex.example.json config.json
|
||||
- freqtrade create-userdir --userdir user_data
|
||||
- freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily
|
||||
name: hyperopt
|
||||
|
@ -12,7 +12,7 @@ Few pointers for contributions:
|
||||
- New features need to contain unit tests, must conform to PEP8 (max-line-length = 100) and should be documented with the introduction PR.
|
||||
- PR's can be declared as `[WIP]` - which signify Work in Progress Pull Requests (which are not finished).
|
||||
|
||||
If you are unsure, discuss the feature on our [discord server](https://discord.gg/p7nuUNVfP7), on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw) or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a PR.
|
||||
If you are unsure, discuss the feature on our [discord server](https://discord.gg/p7nuUNVfP7) or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a Pull Request.
|
||||
|
||||
## Getting started
|
||||
|
||||
|
10
README.md
10
README.md
@ -142,13 +142,9 @@ The project is currently setup in two main branches:
|
||||
|
||||
## Support
|
||||
|
||||
### Help / Discord / Slack
|
||||
### Help / Discord
|
||||
|
||||
For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join our slack channel.
|
||||
|
||||
Please check out our [discord server](https://discord.gg/p7nuUNVfP7).
|
||||
|
||||
You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw).
|
||||
For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join the Freqtrade [discord server](https://discord.gg/p7nuUNVfP7).
|
||||
|
||||
### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue)
|
||||
|
||||
@ -179,7 +175,7 @@ to understand the requirements before sending your pull-requests.
|
||||
Coding is not a necessity to contribute - maybe start with improving our documentation?
|
||||
Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/good%20first%20issue) can be good first contributions, and will help get you familiar with the codebase.
|
||||
|
||||
**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [discord](https://discord.gg/p7nuUNVfP7) or [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it.
|
||||
**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [discord](https://discord.gg/p7nuUNVfP7) (please use the #dev channel for this). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it.
|
||||
|
||||
**Important:** Always create your PR against the `develop` branch, not `stable`.
|
||||
|
||||
|
@ -52,7 +52,7 @@ docker build --cache-from freqtrade:${TAG} --build-arg sourceimage=${TAG} -t fre
|
||||
docker tag freqtrade:$TAG_PLOT ${IMAGE_NAME}:$TAG_PLOT
|
||||
|
||||
# Run backtest
|
||||
docker run --rm -v $(pwd)/config_bittrex.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy
|
||||
docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "failed running backtest"
|
||||
|
@ -52,6 +52,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
||||
| `stake_currency` | **Required.** Crypto-currency used for trading. <br> **Datatype:** String
|
||||
| `stake_amount` | **Required.** Amount of crypto-currency your bot will use for each trade. Set it to `"unlimited"` to allow the bot to use all available balance. [More information below](#configuring-amount-per-trade). <br> **Datatype:** Positive float or `"unlimited"`.
|
||||
| `tradable_balance_ratio` | Ratio of the total account balance the bot is allowed to trade. [More information below](#configuring-amount-per-trade). <br>*Defaults to `0.99` 99%).*<br> **Datatype:** Positive float between `0.1` and `1.0`.
|
||||
| `available_capital` | Available starting capital for the bot. Useful when running multiple bots on the same exchange account.[More information below](#configuring-amount-per-trade). <br> **Datatype:** Positive float.
|
||||
| `amend_last_stake_amount` | Use reduced last stake amount if necessary. [More information below](#configuring-amount-per-trade). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
|
||||
| `last_stake_amount_min_ratio` | Defines minimum stake amount that has to be left and executed. Applies only to the last stake amount when it's amended to a reduced value (i.e. if `amend_last_stake_amount` is set to `true`). [More information below](#configuring-amount-per-trade). <br>*Defaults to `0.5`.* <br> **Datatype:** Float (as ratio)
|
||||
| `amount_reserve_percent` | Reserve some amount in min pair stake amount. The bot will reserve `amount_reserve_percent` + stoploss value when calculating min pair stake amount in order to avoid possible trade refusals. <br>*Defaults to `0.05` (5%).* <br> **Datatype:** Positive Float as ratio.
|
||||
@ -164,7 +165,7 @@ Values set in the configuration file always overwrite values set in the strategy
|
||||
|
||||
### Configuring amount per trade
|
||||
|
||||
There are several methods to configure how much of the stake currency the bot will use to enter a trade. All methods respect the [available balance configuration](#available-balance) as explained below.
|
||||
There are several methods to configure how much of the stake currency the bot will use to enter a trade. All methods respect the [available balance configuration](#tradable-balance) as explained below.
|
||||
|
||||
#### Minimum trade stake
|
||||
|
||||
@ -183,7 +184,7 @@ To limit this calculation in case of large stoploss values, the calculated minim
|
||||
!!! Warning
|
||||
Since the limits on exchanges are usually stable and are not updated often, some pairs can show pretty high minimum limits, simply because the price increased a lot since the last limit adjustment by the exchange.
|
||||
|
||||
#### Available balance
|
||||
#### Tradable balance
|
||||
|
||||
By default, the bot assumes that the `complete amount - 1%` is at it's disposal, and when using [dynamic stake amount](#dynamic-stake-amount), it will split the complete balance into `max_open_trades` buckets per trade.
|
||||
Freqtrade will reserve 1% for eventual fees when entering a trade and will therefore not touch that by default.
|
||||
@ -192,9 +193,25 @@ You can configure the "untouched" amount by using the `tradable_balance_ratio` s
|
||||
|
||||
For example, if you have 10 ETH available in your wallet on the exchange and `tradable_balance_ratio=0.5` (which is 50%), then the bot will use a maximum amount of 5 ETH for trading and considers this as available balance. The rest of the wallet is untouched by the trades.
|
||||
|
||||
!!! Danger
|
||||
This setting should **not** be used when running multiple bots on the same account. Please look at [Available Capital to the bot](#assign-available-capital) instead.
|
||||
|
||||
!!! Warning
|
||||
The `tradable_balance_ratio` setting applies to the current balance (free balance + tied up in trades). Therefore, assuming the starting balance of 1000, a configuration with `tradable_balance_ratio=0.99` will not guarantee that 10 currency units will always remain available on the exchange. For example, the free amount may reduce to 5 units if the total balance is reduced to 500 (either by a losing streak, or by withdrawing balance).
|
||||
|
||||
#### Assign available Capital
|
||||
|
||||
To fully utilize compounding profits when using multiple bots on the same exchange account, you'll want to limit each bot to a certain starting balance.
|
||||
This can be accomplished by setting `available_capital` to the desired starting balance.
|
||||
|
||||
Assuming your account has 10.000 USDT and you want to run 2 different strategies on this exchange.
|
||||
You'd set `available_capital=5000` - granting each bot an initial capital of 5000 USDT.
|
||||
The bot will then split this starting balance equally into `max_open_trades` buckets.
|
||||
Profitable trades will result in increased stake-sizes for this bot - without affecting stake-sizes of the other bot.
|
||||
|
||||
!!! Warning "Incompatible with `tradable_balance_ratio`"
|
||||
Setting this option will replace any configuration of `tradable_balance_ratio`.
|
||||
|
||||
#### Amend last stake amount
|
||||
|
||||
Assuming we have the tradable balance of 1000 USDT, `stake_amount=400`, and `max_open_trades=3`.
|
||||
@ -556,7 +573,7 @@ You should also make sure to read the [Exchanges](exchanges.md) section of the d
|
||||
|
||||
To use a proxy with freqtrade, add the kwarg `"aiohttp_trust_env"=true` to the `"ccxt_async_kwargs"` dict in the exchange section of the configuration.
|
||||
|
||||
An example for this can be found in `config_full.json.example`
|
||||
An example for this can be found in `config_examples/config_full.example.json`
|
||||
|
||||
``` json
|
||||
"ccxt_async_config": {
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
This page is intended for developers of Freqtrade, people who want to contribute to the Freqtrade codebase or documentation, or people who want to understand the source code of the application they're running.
|
||||
|
||||
All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel on [discord](https://discord.gg/p7nuUNVfP7) or [slack](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw) where you can ask questions.
|
||||
All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel on [discord](https://discord.gg/p7nuUNVfP7) where you can ask questions.
|
||||
|
||||
## Documentation
|
||||
|
||||
|
@ -172,7 +172,7 @@ freqtrade hyperopt --hyperopt SampleHyperopt --hyperopt-loss SharpeHyperOptLossD
|
||||
|
||||
### Why does it take a long time to run hyperopt?
|
||||
|
||||
* Discovering a great strategy with Hyperopt takes time. Study www.freqtrade.io, the Freqtrade Documentation page, join the Freqtrade [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw) - or the Freqtrade [discord community](https://discord.gg/p7nuUNVfP7). While you patiently wait for the most advanced, free crypto bot in the world, to hand you a possible golden strategy specially designed just for you.
|
||||
* Discovering a great strategy with Hyperopt takes time. Study www.freqtrade.io, the Freqtrade Documentation page, join the Freqtrade [discord community](https://discord.gg/p7nuUNVfP7). While you patiently wait for the most advanced, free crypto bot in the world, to hand you a possible golden strategy specially designed just for you.
|
||||
|
||||
* If you wonder why it can take from 20 minutes to days to do 1000 epochs here are some answers:
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
## Protections
|
||||
|
||||
!!! Warning "Beta feature"
|
||||
This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord, Slack or via Github Issue.
|
||||
This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord or via Github Issue.
|
||||
|
||||
Protections will protect your strategy from unexpected events and market conditions by temporarily stop trading for either one pair, or for all pairs.
|
||||
All protection end times are rounded up to the next candle to avoid sudden, unexpected intra-candle buys.
|
||||
|
@ -73,13 +73,9 @@ Alternatively
|
||||
|
||||
## Support
|
||||
|
||||
### Help / Discord / Slack
|
||||
### Help / Discord
|
||||
|
||||
For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join our slack channel.
|
||||
|
||||
Please check out our [discord server](https://discord.gg/p7nuUNVfP7).
|
||||
|
||||
You can also join our [Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/zt-mm786y93-Fxo37glxMY9g8OQC5AoOIw).
|
||||
For any questions not covered by the documentation or for further information about the bot, or to simply engage with like-minded individuals, we encourage you to join the Freqtrade [discord server](https://discord.gg/p7nuUNVfP7).
|
||||
|
||||
## Ready to try?
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
mkdocs==1.2.1
|
||||
mkdocs-material==7.1.9
|
||||
mkdocs-material==7.1.10
|
||||
mdx_truly_sane_lists==1.2
|
||||
pymdown-extensions==8.2
|
||||
|
@ -521,6 +521,39 @@ class AwesomeStrategy(IStrategy):
|
||||
|
||||
```
|
||||
|
||||
### Stake size management
|
||||
|
||||
It is possible to manage your risk by reducing or increasing stake amount when placing a new trade.
|
||||
|
||||
```python
|
||||
class AwesomeStrategy(IStrategy):
|
||||
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
|
||||
proposed_stake: float, min_stake: float, max_stake: float,
|
||||
**kwargs) -> float:
|
||||
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
|
||||
current_candle = dataframe.iloc[-1].squeeze()
|
||||
|
||||
if current_candle['fastk_rsi_1h'] > current_candle['fastd_rsi_1h']:
|
||||
if self.config['stake_amount'] == 'unlimited':
|
||||
# Use entire available wallet during favorable conditions when in compounding mode.
|
||||
return max_stake
|
||||
else:
|
||||
# Compound profits during favorable conditions instead of using a static stake.
|
||||
return self.wallets.get_total_stake_amount() / self.config['max_open_trades']
|
||||
|
||||
# Use default stake amount.
|
||||
return proposed_stake
|
||||
```
|
||||
|
||||
Freqtrade will fall back to the `proposed_stake` value should your code raise an exception. The exception itself will be logged.
|
||||
|
||||
!!! Tip
|
||||
You do not _have_ to ensure that `min_stake <= returned_value <= max_stake`. Trades will succeed as the returned value will be clamped to supported range and this acton will be logged.
|
||||
|
||||
!!! Tip
|
||||
Returning `0` or `None` will prevent trades from being placed.
|
||||
|
||||
---
|
||||
|
||||
## Derived strategies
|
||||
|
@ -245,10 +245,10 @@ current max
|
||||
Return a summary of your profit/loss and performance.
|
||||
|
||||
> **ROI:** Close trades
|
||||
> ∙ `0.00485701 BTC (258.45%)`
|
||||
> ∙ `0.00485701 BTC (2.2%) (15.2 Σ%)`
|
||||
> ∙ `62.968 USD`
|
||||
> **ROI:** All trades
|
||||
> ∙ `0.00255280 BTC (143.43%)`
|
||||
> ∙ `0.00255280 BTC (1.5%) (6.43 Σ%)`
|
||||
> ∙ `33.095 EUR`
|
||||
>
|
||||
> **Total Trade Count:** `138`
|
||||
@ -257,6 +257,10 @@ Return a summary of your profit/loss and performance.
|
||||
> **Avg. Duration:** `2:33:45`
|
||||
> **Best Performing:** `PAY/BTC: 50.23%`
|
||||
|
||||
The relative profit of `1.2%` is the average profit per trade.
|
||||
The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`.
|
||||
Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits.
|
||||
|
||||
### /forcesell <trade_id>
|
||||
|
||||
> **BITTREX:** Selling BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)`
|
||||
|
@ -14,7 +14,7 @@ from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.exchange import market_is_active, validate_exchanges
|
||||
from freqtrade.misc import plural
|
||||
from freqtrade.misc import parse_db_uri_for_logging, plural
|
||||
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
||||
|
||||
|
||||
@ -226,7 +226,7 @@ def start_show_trades(args: Dict[str, Any]) -> None:
|
||||
if 'db_url' not in config:
|
||||
raise OperationalException("--db-url is required for this command.")
|
||||
|
||||
logger.info(f'Using DB: "{config["db_url"]}"')
|
||||
logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"')
|
||||
init_db(config['db_url'], clean_open_orders=False)
|
||||
tfilter = []
|
||||
|
||||
|
@ -15,7 +15,7 @@ from freqtrade.configuration.load_config import load_config_file, load_file
|
||||
from freqtrade.enums import NON_UTIL_MODES, TRADING_MODES, RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.loggers import setup_logging
|
||||
from freqtrade.misc import deep_merge_dicts
|
||||
from freqtrade.misc import deep_merge_dicts, parse_db_uri_for_logging
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -144,7 +144,7 @@ class Configuration:
|
||||
config['db_url'] = constants.DEFAULT_DB_PROD_URL
|
||||
logger.info('Dry run is disabled')
|
||||
|
||||
logger.info(f'Using DB: "{config["db_url"]}"')
|
||||
logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"')
|
||||
|
||||
def _process_common_options(self, config: Dict[str, Any]) -> None:
|
||||
|
||||
|
@ -113,6 +113,10 @@ CONF_SCHEMA = {
|
||||
'maximum': 1,
|
||||
'default': 0.99
|
||||
},
|
||||
'available_capital': {
|
||||
'type': 'number',
|
||||
'minimum': 0,
|
||||
},
|
||||
'amend_last_stake_amount': {'type': 'boolean', 'default': False},
|
||||
'last_stake_amount_min_ratio': {
|
||||
'type': 'number', 'minimum': 0.0, 'maximum': 1.0, 'default': 0.5
|
||||
|
@ -424,16 +424,10 @@ class FreqtradeBot(LoggingMixin):
|
||||
|
||||
if buy and not sell:
|
||||
stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge)
|
||||
if not stake_amount:
|
||||
logger.debug(f"Stake amount is 0, ignoring possible trade for {pair}.")
|
||||
return False
|
||||
|
||||
logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: "
|
||||
f"{stake_amount} ...")
|
||||
|
||||
bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {})
|
||||
if ((bid_check_dom.get('enabled', False)) and
|
||||
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
|
||||
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
|
||||
if self._check_depth_of_market_buy(pair, bid_check_dom):
|
||||
return self.execute_buy(pair, stake_amount)
|
||||
else:
|
||||
@ -488,13 +482,22 @@ class FreqtradeBot(LoggingMixin):
|
||||
|
||||
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, buy_limit_requested,
|
||||
self.strategy.stoploss)
|
||||
if min_stake_amount is not None and min_stake_amount > stake_amount:
|
||||
logger.warning(
|
||||
f"Can't open a new trade for {pair}: stake amount "
|
||||
f"is too small ({stake_amount} < {min_stake_amount})"
|
||||
)
|
||||
|
||||
if not self.edge:
|
||||
max_stake_amount = self.wallets.get_available_stake_amount()
|
||||
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
|
||||
default_retval=stake_amount)(
|
||||
pair=pair, current_time=datetime.now(timezone.utc),
|
||||
current_rate=buy_limit_requested, proposed_stake=stake_amount,
|
||||
min_stake=min_stake_amount, max_stake=max_stake_amount)
|
||||
stake_amount = self.wallets._validate_stake_amount(pair, stake_amount, min_stake_amount)
|
||||
|
||||
if not stake_amount:
|
||||
return False
|
||||
|
||||
logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: "
|
||||
f"{stake_amount} ...")
|
||||
|
||||
amount = stake_amount / buy_limit_requested
|
||||
order_type = self.strategy.order_types['buy']
|
||||
if forcebuy:
|
||||
|
@ -8,6 +8,7 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterator, List
|
||||
from typing.io import IO
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import rapidjson
|
||||
|
||||
@ -214,3 +215,16 @@ def chunks(lst: List[Any], n: int) -> Iterator[List[Any]]:
|
||||
"""
|
||||
for chunk in range(0, len(lst), n):
|
||||
yield (lst[chunk:chunk + n])
|
||||
|
||||
|
||||
def parse_db_uri_for_logging(uri: str):
|
||||
"""
|
||||
Helper method to parse the DB URI and return the same DB URI with the password censored
|
||||
if it contains it. Otherwise, return the DB URI unchanged
|
||||
:param uri: DB URI to parse for logging
|
||||
"""
|
||||
parsed_db_uri = urlparse(uri)
|
||||
if not parsed_db_uri.netloc: # No need for censoring as no password was provided
|
||||
return uri
|
||||
pwd = parsed_db_uri.netloc.split(':')[1].split('@')[0]
|
||||
return parsed_db_uri.geturl().replace(f':{pwd}@', ':*****@')
|
||||
|
@ -129,6 +129,8 @@ class Backtesting:
|
||||
"""
|
||||
self.strategy: IStrategy = strategy
|
||||
strategy.dp = self.dataprovider
|
||||
# Attach Wallets to Strategy baseclass
|
||||
IStrategy.wallets = self.wallets
|
||||
# Set stoploss_on_exchange to false for backtesting,
|
||||
# since a "perfect" stoploss-sell is assumed anyway
|
||||
# And the regular "stoploss" function would not apply to that case
|
||||
@ -312,7 +314,18 @@ class Backtesting:
|
||||
stake_amount = self.wallets.get_trade_stake_amount(pair, None)
|
||||
except DependencyException:
|
||||
return None
|
||||
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, row[OPEN_IDX], -0.05)
|
||||
|
||||
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, row[OPEN_IDX], -0.05) or 0
|
||||
max_stake_amount = self.wallets.get_available_stake_amount()
|
||||
|
||||
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
|
||||
default_retval=stake_amount)(
|
||||
pair=pair, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX],
|
||||
proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount)
|
||||
stake_amount = self.wallets._validate_stake_amount(pair, stake_amount, min_stake_amount)
|
||||
|
||||
if not stake_amount:
|
||||
return None
|
||||
|
||||
order_type = self.strategy.order_types['buy']
|
||||
time_in_force = self.strategy.order_time_in_force['sell']
|
||||
|
@ -987,6 +987,19 @@ class Trade(_DECL_BASE, LocalTrade):
|
||||
Trade.is_open.is_(False),
|
||||
]).all()
|
||||
|
||||
@staticmethod
|
||||
def get_total_closed_profit() -> float:
|
||||
"""
|
||||
Retrieves total realized profit
|
||||
"""
|
||||
if Trade.use_db:
|
||||
total_profit = Trade.query.with_entities(
|
||||
func.sum(Trade.close_profit_abs)).filter(Trade.is_open.is_(False)).scalar()
|
||||
else:
|
||||
total_profit = sum(
|
||||
t.close_profit_abs for t in LocalTrade.get_trades_proxy(is_open=False))
|
||||
return total_profit or 0
|
||||
|
||||
@staticmethod
|
||||
def total_open_trades_stakes() -> float:
|
||||
"""
|
||||
|
@ -67,12 +67,16 @@ class Profit(BaseModel):
|
||||
profit_closed_ratio_mean: float
|
||||
profit_closed_percent_sum: float
|
||||
profit_closed_ratio_sum: float
|
||||
profit_closed_percent: float
|
||||
profit_closed_ratio: float
|
||||
profit_closed_fiat: float
|
||||
profit_all_coin: float
|
||||
profit_all_percent_mean: float
|
||||
profit_all_ratio_mean: float
|
||||
profit_all_percent_sum: float
|
||||
profit_all_ratio_sum: float
|
||||
profit_all_percent: float
|
||||
profit_all_ratio: float
|
||||
profit_all_fiat: float
|
||||
trade_count: int
|
||||
closed_trade_count: int
|
||||
@ -115,6 +119,7 @@ class ShowConfig(BaseModel):
|
||||
dry_run: bool
|
||||
stake_currency: str
|
||||
stake_amount: Union[float, str]
|
||||
available_capital: Optional[float]
|
||||
stake_currency_decimals: int
|
||||
max_open_trades: int
|
||||
minimal_roi: Dict[str, Any]
|
||||
|
@ -106,6 +106,7 @@ class RPC:
|
||||
'stake_currency': config['stake_currency'],
|
||||
'stake_currency_decimals': decimals_per_coin(config['stake_currency']),
|
||||
'stake_amount': config['stake_amount'],
|
||||
'available_capital': config.get('available_capital'),
|
||||
'max_open_trades': (config['max_open_trades']
|
||||
if config['max_open_trades'] != float('inf') else -1),
|
||||
'minimal_roi': config['minimal_roi'].copy() if 'minimal_roi' in config else {},
|
||||
@ -396,7 +397,12 @@ class RPC:
|
||||
|
||||
profit_all_coin_sum = round(sum(profit_all_coin), 8)
|
||||
profit_all_ratio_mean = float(mean(profit_all_ratio) if profit_all_ratio else 0.0)
|
||||
# Doing the sum is not right - overall profit needs to be based on initial capital
|
||||
profit_all_ratio_sum = sum(profit_all_ratio) if profit_all_ratio else 0.0
|
||||
starting_balance = self._freqtrade.wallets.get_starting_balance()
|
||||
profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance
|
||||
profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance
|
||||
|
||||
profit_all_fiat = self._fiat_converter.convert_amount(
|
||||
profit_all_coin_sum,
|
||||
stake_currency,
|
||||
@ -412,12 +418,16 @@ class RPC:
|
||||
'profit_closed_ratio_mean': profit_closed_ratio_mean,
|
||||
'profit_closed_percent_sum': round(profit_closed_ratio_sum * 100, 2),
|
||||
'profit_closed_ratio_sum': profit_closed_ratio_sum,
|
||||
'profit_closed_ratio': profit_closed_ratio_fromstart,
|
||||
'profit_closed_percent': round(profit_closed_ratio_fromstart * 100, 2),
|
||||
'profit_closed_fiat': profit_closed_fiat,
|
||||
'profit_all_coin': profit_all_coin_sum,
|
||||
'profit_all_percent_mean': round(profit_all_ratio_mean * 100, 2),
|
||||
'profit_all_ratio_mean': profit_all_ratio_mean,
|
||||
'profit_all_percent_sum': round(profit_all_ratio_sum * 100, 2),
|
||||
'profit_all_ratio_sum': profit_all_ratio_sum,
|
||||
'profit_all_ratio': profit_all_ratio_fromstart,
|
||||
'profit_all_percent': round(profit_all_ratio_fromstart * 100, 2),
|
||||
'profit_all_fiat': profit_all_fiat,
|
||||
'trade_count': len(trades),
|
||||
'closed_trade_count': len([t for t in trades if not t.is_open]),
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""
|
||||
This module contains class to manage RPC communications (Telegram, Slack, ...)
|
||||
This module contains class to manage RPC communications (Telegram, API, ...)
|
||||
"""
|
||||
import logging
|
||||
from typing import Any, Dict, List
|
||||
@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class RPCManager:
|
||||
"""
|
||||
Class to manage RPC objects (Telegram, Slack, ...)
|
||||
Class to manage RPC objects (Telegram, API, ...)
|
||||
"""
|
||||
# TODO-mg: Add new configuration options introduced with leveraged/short trading
|
||||
def __init__(self, freqtrade) -> None:
|
||||
|
@ -494,11 +494,11 @@ class Telegram(RPCHandler):
|
||||
start_date)
|
||||
profit_closed_coin = stats['profit_closed_coin']
|
||||
profit_closed_percent_mean = stats['profit_closed_percent_mean']
|
||||
profit_closed_percent_sum = stats['profit_closed_percent_sum']
|
||||
profit_closed_percent = stats['profit_closed_percent']
|
||||
profit_closed_fiat = stats['profit_closed_fiat']
|
||||
profit_all_coin = stats['profit_all_coin']
|
||||
profit_all_percent_mean = stats['profit_all_percent_mean']
|
||||
profit_all_percent_sum = stats['profit_all_percent_sum']
|
||||
profit_all_percent = stats['profit_all_percent']
|
||||
profit_all_fiat = stats['profit_all_fiat']
|
||||
trade_count = stats['trade_count']
|
||||
first_trade_date = stats['first_trade_date']
|
||||
@ -514,7 +514,7 @@ class Telegram(RPCHandler):
|
||||
markdown_msg = ("*ROI:* Closed trades\n"
|
||||
f"∙ `{round_coin_value(profit_closed_coin, stake_cur)} "
|
||||
f"({profit_closed_percent_mean:.2f}%) "
|
||||
f"({profit_closed_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
|
||||
f"({profit_closed_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
|
||||
f"∙ `{round_coin_value(profit_closed_fiat, fiat_disp_cur)}`\n")
|
||||
else:
|
||||
markdown_msg = "`No closed trade` \n"
|
||||
@ -523,7 +523,7 @@ class Telegram(RPCHandler):
|
||||
f"*ROI:* All trades\n"
|
||||
f"∙ `{round_coin_value(profit_all_coin, stake_cur)} "
|
||||
f"({profit_all_percent_mean:.2f}%) "
|
||||
f"({profit_all_percent_sum} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
|
||||
f"({profit_all_percent} \N{GREEK CAPITAL LETTER SIGMA}%)`\n"
|
||||
f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n"
|
||||
f"*Total Trade Count:* `{trade_count}`\n"
|
||||
f"*{'First Trade opened' if not timescale else 'Showing Profit since'}:* "
|
||||
|
@ -304,6 +304,23 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
"""
|
||||
return None
|
||||
|
||||
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
|
||||
proposed_stake: float, min_stake: float, max_stake: float,
|
||||
**kwargs) -> float:
|
||||
"""
|
||||
Customize stake size for each new trade. This method is not called when edge module is
|
||||
enabled.
|
||||
|
||||
:param pair: Pair that's currently analyzed
|
||||
:param current_time: datetime object, containing the current datetime
|
||||
:param current_rate: Rate, calculated based on pricing settings in ask_strategy.
|
||||
:param proposed_stake: A stake amount proposed by the bot.
|
||||
:param min_stake: Minimal stake size allowed by exchange.
|
||||
:param max_stake: Balance available for trading.
|
||||
:return: A stake size, which is between min_stake and max_stake.
|
||||
"""
|
||||
return proposed_stake
|
||||
|
||||
def informative_pairs(self) -> ListPairsWithTimeframes:
|
||||
"""
|
||||
Define additional, informative pair/interval combinations to be cached from the exchange.
|
||||
|
@ -70,9 +70,7 @@ class Wallets:
|
||||
# If not backtesting...
|
||||
# TODO: potentially remove the ._log workaround to determine backtest mode.
|
||||
if self._log:
|
||||
closed_trades = Trade.get_trades_proxy(is_open=False)
|
||||
tot_profit = sum(
|
||||
[trade.close_profit_abs for trade in closed_trades if trade.close_profit_abs])
|
||||
tot_profit = Trade.get_total_closed_profit()
|
||||
else:
|
||||
tot_profit = LocalTrade.total_profit
|
||||
tot_in_trades = sum([trade.stake_amount for trade in open_trades])
|
||||
@ -131,7 +129,41 @@ class Wallets:
|
||||
def get_all_balances(self) -> Dict[str, Any]:
|
||||
return self._wallets
|
||||
|
||||
def _get_available_stake_amount(self, val_tied_up: float) -> float:
|
||||
def get_starting_balance(self) -> float:
|
||||
"""
|
||||
Retrieves starting balance - based on either available capital,
|
||||
or by using current balance subtracting
|
||||
"""
|
||||
if "available_capital" in self._config:
|
||||
return self._config['available_capital']
|
||||
else:
|
||||
tot_profit = Trade.get_total_closed_profit()
|
||||
open_stakes = Trade.total_open_trades_stakes()
|
||||
available_balance = self.get_free(self._config['stake_currency'])
|
||||
return available_balance - tot_profit + open_stakes
|
||||
|
||||
def get_total_stake_amount(self):
|
||||
"""
|
||||
Return the total currently available balance in stake currency, including tied up stake and
|
||||
respecting tradable_balance_ratio.
|
||||
Calculated as
|
||||
(<open_trade stakes> + free amount) * tradable_balance_ratio
|
||||
"""
|
||||
val_tied_up = Trade.total_open_trades_stakes()
|
||||
if "available_capital" in self._config:
|
||||
starting_balance = self._config['available_capital']
|
||||
tot_profit = Trade.get_total_closed_profit()
|
||||
available_amount = starting_balance + tot_profit
|
||||
|
||||
else:
|
||||
# Ensure <tradable_balance_ratio>% is used from the overall balance
|
||||
# Otherwise we'd risk lowering stakes with each open trade.
|
||||
# (tied up + current free) * ratio) - tied up
|
||||
available_amount = ((val_tied_up + self.get_free(self._config['stake_currency'])) *
|
||||
self._config['tradable_balance_ratio'])
|
||||
return available_amount
|
||||
|
||||
def get_available_stake_amount(self) -> float:
|
||||
"""
|
||||
Return the total currently available balance in stake currency,
|
||||
respecting tradable_balance_ratio.
|
||||
@ -139,12 +171,8 @@ class Wallets:
|
||||
(<open_trade stakes> + free amount) * tradable_balance_ratio - <open_trade stakes>
|
||||
"""
|
||||
|
||||
# Ensure <tradable_balance_ratio>% is used from the overall balance
|
||||
# Otherwise we'd risk lowering stakes with each open trade.
|
||||
# (tied up + current free) * ratio) - tied up
|
||||
available_amount = ((val_tied_up + self.get_free(self._config['stake_currency'])) *
|
||||
self._config['tradable_balance_ratio']) - val_tied_up
|
||||
return available_amount
|
||||
free = self.get_free(self._config['stake_currency'])
|
||||
return min(self.get_total_stake_amount() - Trade.total_open_trades_stakes(), free)
|
||||
|
||||
def _calculate_unlimited_stake_amount(self, available_amount: float,
|
||||
val_tied_up: float) -> float:
|
||||
@ -193,7 +221,7 @@ class Wallets:
|
||||
# Ensure wallets are uptodate.
|
||||
self.update()
|
||||
val_tied_up = Trade.total_open_trades_stakes()
|
||||
available_amount = self._get_available_stake_amount(val_tied_up)
|
||||
available_amount = self.get_available_stake_amount()
|
||||
|
||||
if edge:
|
||||
stake_amount = edge.stake_amount(
|
||||
@ -209,3 +237,30 @@ class Wallets:
|
||||
available_amount, val_tied_up)
|
||||
|
||||
return self._check_available_stake_amount(stake_amount, available_amount)
|
||||
|
||||
def _validate_stake_amount(self, pair, stake_amount, min_stake_amount):
|
||||
if not stake_amount:
|
||||
logger.debug(f"Stake amount is {stake_amount}, ignoring possible trade for {pair}.")
|
||||
return 0
|
||||
|
||||
max_stake_amount = self.get_available_stake_amount()
|
||||
|
||||
if min_stake_amount > max_stake_amount:
|
||||
if self._log:
|
||||
logger.warning("Minimum stake amount > available balance.")
|
||||
return 0
|
||||
if min_stake_amount is not None and stake_amount < min_stake_amount:
|
||||
stake_amount = min_stake_amount
|
||||
if self._log:
|
||||
logger.info(
|
||||
f"Stake amount for pair {pair} is too small "
|
||||
f"({stake_amount} < {min_stake_amount}), adjusting to {min_stake_amount}."
|
||||
)
|
||||
if stake_amount > max_stake_amount:
|
||||
stake_amount = max_stake_amount
|
||||
if self._log:
|
||||
logger.info(
|
||||
f"Stake amount for pair {pair} is too big "
|
||||
f"({stake_amount} > {max_stake_amount}), adjusting to {max_stake_amount}."
|
||||
)
|
||||
return stake_amount
|
||||
|
@ -13,7 +13,7 @@ pytest-asyncio==0.15.1
|
||||
pytest-cov==2.12.1
|
||||
pytest-mock==3.6.1
|
||||
pytest-random-order==1.0.4
|
||||
isort==5.9.1
|
||||
isort==5.9.2
|
||||
|
||||
# Convert jupyter notebooks to markdown documents
|
||||
nbconvert==6.1.0
|
||||
|
@ -1,7 +1,7 @@
|
||||
numpy==1.21.0
|
||||
pandas==1.3.0
|
||||
|
||||
ccxt==1.52.40
|
||||
ccxt==1.52.83
|
||||
# Pin cryptography for now due to rust build errors with piwheels
|
||||
cryptography==3.4.7
|
||||
aiohttp==3.7.4.post0
|
||||
|
78
setup.sh
78
setup.sh
@ -17,35 +17,20 @@ function check_installed_python() {
|
||||
exit 2
|
||||
fi
|
||||
|
||||
which python3.8
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "using Python 3.8"
|
||||
PYTHON=python3.8
|
||||
check_installed_pip
|
||||
return
|
||||
fi
|
||||
for v in 8 9 7
|
||||
do
|
||||
PYTHON="python3.${v}"
|
||||
which $PYTHON
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "using ${PYTHON}"
|
||||
|
||||
which python3.9
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "using Python 3.9"
|
||||
PYTHON=python3.9
|
||||
check_installed_pip
|
||||
return
|
||||
fi
|
||||
check_installed_pip
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
which python3.7
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "using Python 3.7"
|
||||
PYTHON=python3.7
|
||||
check_installed_pip
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
if [ -z ${PYTHON} ]; then
|
||||
echo "No usable python found. Please make sure to have python3.7 or newer installed"
|
||||
exit 1
|
||||
fi
|
||||
echo "No usable python found. Please make sure to have python3.7 or newer installed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function updateenv() {
|
||||
@ -122,6 +107,25 @@ function install_talib() {
|
||||
cd ..
|
||||
}
|
||||
|
||||
function install_mac_newer_python_dependencies() {
|
||||
|
||||
if [ ! $(brew --prefix --installed hdf5 2>/dev/null) ]
|
||||
then
|
||||
echo "-------------------------"
|
||||
echo "Installing hdf5"
|
||||
echo "-------------------------"
|
||||
brew install hdf5
|
||||
fi
|
||||
|
||||
if [ ! $(brew --prefix --installed c-blosc 2>/dev/null) ]
|
||||
then
|
||||
echo "-------------------------"
|
||||
echo "Installing c-blosc"
|
||||
echo "-------------------------"
|
||||
brew install c-blosc
|
||||
fi
|
||||
}
|
||||
|
||||
# Install bot MacOS
|
||||
function install_macos() {
|
||||
if [ ! -x "$(command -v brew)" ]
|
||||
@ -131,8 +135,13 @@ function install_macos() {
|
||||
echo "-------------------------"
|
||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
fi
|
||||
#Gets number after decimal in python version
|
||||
version=$(egrep -o 3.\[0-9\]+ <<< $PYTHON | sed 's/3.//g' )
|
||||
|
||||
if [[ $version -ge 9 ]]; then #Checks if python version >= 3.9
|
||||
install_mac_newer_python_dependencies
|
||||
fi
|
||||
install_talib
|
||||
test_and_fix_python_on_mac
|
||||
}
|
||||
|
||||
# Install bot Debian_ubuntu
|
||||
@ -189,19 +198,6 @@ function reset() {
|
||||
updateenv
|
||||
}
|
||||
|
||||
function test_and_fix_python_on_mac() {
|
||||
|
||||
if ! [ -x "$(command -v python3.6)" ]
|
||||
then
|
||||
echo "-------------------------"
|
||||
echo "Fixing Python"
|
||||
echo "-------------------------"
|
||||
echo "Python 3.6 is not linked in your system. Fixing it..."
|
||||
brew link --overwrite python
|
||||
echo
|
||||
fi
|
||||
}
|
||||
|
||||
function config() {
|
||||
|
||||
echo "-------------------------"
|
||||
|
@ -26,7 +26,7 @@ from tests.conftest_trades import MOCK_TRADE_COUNT
|
||||
|
||||
def test_setup_utils_configuration():
|
||||
args = [
|
||||
'list-exchanges', '--config', 'config_bittrex.json.example',
|
||||
'list-exchanges', '--config', 'config_examples/config_bittrex.example.json',
|
||||
]
|
||||
|
||||
config = setup_utils_configuration(get_args(args), RunMode.OTHER)
|
||||
@ -45,7 +45,7 @@ def test_start_trading_fail(mocker, caplog):
|
||||
exitmock = mocker.patch("freqtrade.worker.Worker.exit", MagicMock())
|
||||
args = [
|
||||
'trade',
|
||||
'-c', 'config_bittrex.json.example'
|
||||
'-c', 'config_examples/config_bittrex.example.json'
|
||||
]
|
||||
start_trading(get_args(args))
|
||||
assert exitmock.call_count == 1
|
||||
@ -127,10 +127,10 @@ def test_list_timeframes(mocker, capsys):
|
||||
match=r"This command requires a configured exchange.*"):
|
||||
start_list_timeframes(pargs)
|
||||
|
||||
# Test with --config config_bittrex.json.example
|
||||
# Test with --config config_examples/config_bittrex.example.json
|
||||
args = [
|
||||
"list-timeframes",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
]
|
||||
start_list_timeframes(get_args(args))
|
||||
captured = capsys.readouterr()
|
||||
@ -174,7 +174,7 @@ def test_list_timeframes(mocker, capsys):
|
||||
# Test with --one-column
|
||||
args = [
|
||||
"list-timeframes",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--one-column",
|
||||
]
|
||||
start_list_timeframes(get_args(args))
|
||||
@ -214,10 +214,10 @@ def test_list_markets(mocker, markets, capsys):
|
||||
match=r"This command requires a configured exchange.*"):
|
||||
start_list_markets(pargs, False)
|
||||
|
||||
# Test with --config config_bittrex.json.example
|
||||
# Test with --config config_examples/config_bittrex.example.json
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
@ -244,7 +244,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# Test with --all: all markets
|
||||
args = [
|
||||
"list-markets", "--all",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
@ -257,7 +257,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# Test list-pairs subcommand: active pairs
|
||||
args = [
|
||||
"list-pairs",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), True)
|
||||
@ -269,7 +269,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# Test list-pairs subcommand with --all: all pairs
|
||||
args = [
|
||||
"list-pairs", "--all",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--print-list",
|
||||
]
|
||||
start_list_markets(get_args(args), True)
|
||||
@ -282,7 +282,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# active markets, base=ETH, LTC
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--base", "ETH", "LTC",
|
||||
"--print-list",
|
||||
]
|
||||
@ -295,7 +295,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# active markets, base=LTC
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--base", "LTC",
|
||||
"--print-list",
|
||||
]
|
||||
@ -308,7 +308,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# active markets, quote=USDT, USD
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--quote", "USDT", "USD",
|
||||
"--print-list",
|
||||
]
|
||||
@ -321,7 +321,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# active markets, quote=USDT
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--quote", "USDT",
|
||||
"--print-list",
|
||||
]
|
||||
@ -334,7 +334,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# active markets, base=LTC, quote=USDT
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--base", "LTC", "--quote", "USDT",
|
||||
"--print-list",
|
||||
]
|
||||
@ -347,7 +347,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# active pairs, base=LTC, quote=USDT
|
||||
args = [
|
||||
"list-pairs",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--base", "LTC", "--quote", "USD",
|
||||
"--print-list",
|
||||
]
|
||||
@ -360,7 +360,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# active markets, base=LTC, quote=USDT, NONEXISTENT
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--base", "LTC", "--quote", "USDT", "NONEXISTENT",
|
||||
"--print-list",
|
||||
]
|
||||
@ -373,7 +373,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# active markets, base=LTC, quote=NONEXISTENT
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--base", "LTC", "--quote", "NONEXISTENT",
|
||||
"--print-list",
|
||||
]
|
||||
@ -386,7 +386,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# Test tabular output
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
captured = capsys.readouterr()
|
||||
@ -396,7 +396,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# Test tabular output, no markets found
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--base", "LTC", "--quote", "NONEXISTENT",
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
@ -408,7 +408,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# Test --print-json
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--print-json"
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
@ -420,7 +420,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# Test --print-csv
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--print-csv"
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
@ -432,7 +432,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# Test --one-column
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--one-column"
|
||||
]
|
||||
start_list_markets(get_args(args), False)
|
||||
@ -444,7 +444,7 @@ def test_list_markets(mocker, markets, capsys):
|
||||
# Test --one-column
|
||||
args = [
|
||||
"list-markets",
|
||||
'--config', 'config_bittrex.json.example',
|
||||
'--config', 'config_examples/config_bittrex.example.json',
|
||||
"--one-column"
|
||||
]
|
||||
with pytest.raises(OperationalException, match=r"Cannot get markets.*"):
|
||||
@ -887,7 +887,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
args = [
|
||||
'test-pairlist',
|
||||
'-c', 'config_bittrex.json.example'
|
||||
'-c', 'config_examples/config_bittrex.example.json'
|
||||
]
|
||||
|
||||
start_test_pairlist(get_args(args))
|
||||
@ -901,7 +901,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
|
||||
|
||||
args = [
|
||||
'test-pairlist',
|
||||
'-c', 'config_bittrex.json.example',
|
||||
'-c', 'config_examples/config_bittrex.example.json',
|
||||
'--one-column',
|
||||
]
|
||||
start_test_pairlist(get_args(args))
|
||||
@ -910,7 +910,7 @@ def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):
|
||||
|
||||
args = [
|
||||
'test-pairlist',
|
||||
'-c', 'config_bittrex.json.example',
|
||||
'-c', 'config_examples/config_bittrex.example.json',
|
||||
'--print-json',
|
||||
]
|
||||
start_test_pairlist(get_args(args))
|
||||
|
@ -496,6 +496,17 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
assert trade is not None
|
||||
|
||||
backtesting.strategy.custom_stake_amount = lambda **kwargs: 123.5
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
assert trade
|
||||
assert trade.stake_amount == 123.5
|
||||
|
||||
# In case of error - use proposed stake
|
||||
backtesting.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
|
||||
trade = backtesting._enter_trade(pair, row=row)
|
||||
assert trade
|
||||
assert trade.stake_amount == 495
|
||||
|
||||
# Stake-amount too high!
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0)
|
||||
|
||||
|
@ -1254,6 +1254,21 @@ def test_total_open_trades_stakes(fee, use_db):
|
||||
Trade.use_db = True
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize('use_db', [True, False])
|
||||
def test_get_total_closed_profit(fee, use_db):
|
||||
|
||||
Trade.use_db = use_db
|
||||
Trade.reset_trades()
|
||||
res = Trade.get_total_closed_profit()
|
||||
assert res == 0
|
||||
create_mock_trades(fee, use_db)
|
||||
res = Trade.get_total_closed_profit()
|
||||
assert res == 0.000739127
|
||||
|
||||
Trade.use_db = True
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize('use_db', [True, False])
|
||||
def test_get_trades_proxy(fee, use_db):
|
||||
@ -1428,6 +1443,7 @@ def test_Trade_object_idem():
|
||||
'open_date',
|
||||
'get_best_pair',
|
||||
'get_overall_performance',
|
||||
'get_total_closed_profit',
|
||||
'total_open_trades_stakes',
|
||||
'get_closed_trades_without_assigned_fees',
|
||||
'get_open_trades_without_assigned_fees',
|
||||
|
@ -372,7 +372,7 @@ def test_calc_close_trade_price_lev(market_lev_buy_order, market_lev_sell_order,
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_update_limit_order_lev(limit_lev_buy_order, limit_lev_sell_order, fee, caplog):
|
||||
def test_update_with_binance_lev(limit_lev_buy_order, limit_lev_sell_order, fee, caplog):
|
||||
"""
|
||||
10 minute leveraged limit trade on binance at 3x leverage
|
||||
|
||||
|
@ -894,7 +894,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) ->
|
||||
|
||||
# Test not buying
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
freqtradebot.config['stake_amount'] = 0.0000001
|
||||
freqtradebot.config['stake_amount'] = 0
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
rpc = RPC(freqtradebot)
|
||||
pair = 'TKN/BTC'
|
||||
|
@ -677,12 +677,16 @@ def test_api_profit(botclient, mocker, ticker, fee, markets):
|
||||
'profit_all_ratio_mean': -0.6641100666666667,
|
||||
'profit_all_percent_sum': -398.47,
|
||||
'profit_all_ratio_sum': -3.9846604,
|
||||
'profit_all_percent': -4.41,
|
||||
'profit_all_ratio': -0.044063014216106644,
|
||||
'profit_closed_coin': 0.00073913,
|
||||
'profit_closed_fiat': 9.124559849999999,
|
||||
'profit_closed_ratio_mean': 0.0075,
|
||||
'profit_closed_percent_mean': 0.75,
|
||||
'profit_closed_ratio_sum': 0.015,
|
||||
'profit_closed_percent_sum': 1.5,
|
||||
'profit_closed_ratio': 7.391275897987988e-07,
|
||||
'profit_closed_percent': 0.0,
|
||||
'trade_count': 6,
|
||||
'closed_trade_count': 2,
|
||||
'winning_trades': 2,
|
||||
|
@ -452,7 +452,8 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'No closed trade' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]
|
||||
assert ('∙ `-0.00000500 BTC (-0.50%) (-0.5 \N{GREEK CAPITAL LETTER SIGMA}%)`'
|
||||
mocker.patch('freqtrade.wallets.Wallets.get_starting_balance', return_value=0.01)
|
||||
assert ('∙ `-0.00000500 BTC (-0.50%) (-0.0 \N{GREEK CAPITAL LETTER SIGMA}%)`'
|
||||
in msg_mock.call_args_list[-1][0][0])
|
||||
msg_mock.reset_mock()
|
||||
|
||||
@ -466,11 +467,11 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
telegram._profit(update=update, context=MagicMock())
|
||||
assert msg_mock.call_count == 1
|
||||
assert '*ROI:* Closed trades' in msg_mock.call_args_list[-1][0][0]
|
||||
assert ('∙ `0.00006217 BTC (6.20%) (6.2 \N{GREEK CAPITAL LETTER SIGMA}%)`'
|
||||
assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`'
|
||||
in msg_mock.call_args_list[-1][0][0])
|
||||
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0]
|
||||
assert ('∙ `0.00006217 BTC (6.20%) (6.2 \N{GREEK CAPITAL LETTER SIGMA}%)`'
|
||||
assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`'
|
||||
in msg_mock.call_args_list[-1][0][0])
|
||||
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]
|
||||
|
||||
|
@ -172,7 +172,7 @@ def test_download_data_options() -> None:
|
||||
def test_plot_dataframe_options() -> None:
|
||||
args = [
|
||||
'plot-dataframe',
|
||||
'-c', 'config_bittrex.json.example',
|
||||
'-c', 'config_examples/config_bittrex.example.json',
|
||||
'--indicators1', 'sma10', 'sma100',
|
||||
'--indicators2', 'macd', 'fastd', 'fastk',
|
||||
'--plot-limit', '30',
|
||||
|
@ -28,7 +28,7 @@ from tests.conftest import log_has, log_has_re, patched_configuration_load_confi
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def all_conf():
|
||||
config_file = Path(__file__).parents[1] / "config_full.json.example"
|
||||
config_file = Path(__file__).parents[1] / "config_examples/config_full.example.json"
|
||||
conf = load_config_file(str(config_file))
|
||||
return conf
|
||||
|
||||
|
@ -397,7 +397,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order_open,
|
||||
|
||||
|
||||
def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order_open,
|
||||
fee, mocker) -> None:
|
||||
fee, mocker, caplog) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
buy_mock = MagicMock(return_value=limit_buy_order_open)
|
||||
@ -413,6 +413,27 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord
|
||||
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
assert freqtrade.create_trade('ETH/BTC')
|
||||
assert log_has_re(r"Stake amount for pair .* is too small.*", caplog)
|
||||
|
||||
|
||||
def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open,
|
||||
fee, mocker) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
buy_mock = MagicMock(return_value=limit_buy_order_open)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
fetch_ticker=ticker,
|
||||
buy=buy_mock,
|
||||
get_fee=fee,
|
||||
)
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
freqtrade.config['stake_amount'] = 0
|
||||
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
assert not freqtrade.create_trade('ETH/BTC')
|
||||
|
||||
|
||||
@ -842,6 +863,24 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
|
||||
assert trade.open_rate == 0.5
|
||||
assert trade.stake_amount == 40.495905365
|
||||
|
||||
# Test with custom stake
|
||||
limit_buy_order['status'] = 'open'
|
||||
limit_buy_order['id'] = '556'
|
||||
|
||||
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0
|
||||
assert freqtrade.execute_buy(pair, stake_amount)
|
||||
trade = Trade.query.all()[4]
|
||||
assert trade
|
||||
assert trade.stake_amount == 150
|
||||
|
||||
# Exception case
|
||||
limit_buy_order['id'] = '557'
|
||||
freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
|
||||
assert freqtrade.execute_buy(pair, stake_amount)
|
||||
trade = Trade.query.all()[5]
|
||||
assert trade
|
||||
assert trade.stake_amount == 2.0
|
||||
|
||||
# In case of the order is rejected and not filled at all
|
||||
limit_buy_order['status'] = 'rejected'
|
||||
limit_buy_order['amount'] = 90.99181073
|
||||
|
@ -67,12 +67,12 @@ def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
|
||||
|
||||
args = ['trade', '-c', 'config_bittrex.json.example']
|
||||
args = ['trade', '-c', 'config_examples/config_bittrex.example.json']
|
||||
|
||||
# Test Main + the KeyboardInterrupt exception
|
||||
with pytest.raises(SystemExit):
|
||||
main(args)
|
||||
assert log_has('Using config: config_bittrex.json.example ...', caplog)
|
||||
assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog)
|
||||
assert log_has('Fatal exception!', caplog)
|
||||
|
||||
|
||||
@ -85,12 +85,12 @@ def test_main_keyboard_interrupt(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
|
||||
|
||||
args = ['trade', '-c', 'config_bittrex.json.example']
|
||||
args = ['trade', '-c', 'config_examples/config_bittrex.example.json']
|
||||
|
||||
# Test Main + the KeyboardInterrupt exception
|
||||
with pytest.raises(SystemExit):
|
||||
main(args)
|
||||
assert log_has('Using config: config_bittrex.json.example ...', caplog)
|
||||
assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog)
|
||||
assert log_has('SIGINT received, aborting ...', caplog)
|
||||
|
||||
|
||||
@ -106,12 +106,12 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
|
||||
|
||||
args = ['trade', '-c', 'config_bittrex.json.example']
|
||||
args = ['trade', '-c', 'config_examples/config_bittrex.example.json']
|
||||
|
||||
# Test Main + the KeyboardInterrupt exception
|
||||
with pytest.raises(SystemExit):
|
||||
main(args)
|
||||
assert log_has('Using config: config_bittrex.json.example ...', caplog)
|
||||
assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog)
|
||||
assert log_has('Oh snap!', caplog)
|
||||
|
||||
|
||||
@ -157,12 +157,16 @@ def test_main_reload_config(mocker, default_conf, caplog) -> None:
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
|
||||
|
||||
args = Arguments(['trade', '-c', 'config_bittrex.json.example']).get_parsed_arg()
|
||||
args = Arguments([
|
||||
'trade',
|
||||
'-c',
|
||||
'config_examples/config_bittrex.example.json'
|
||||
]).get_parsed_arg()
|
||||
worker = Worker(args=args, config=default_conf)
|
||||
with pytest.raises(SystemExit):
|
||||
main(['trade', '-c', 'config_bittrex.json.example'])
|
||||
main(['trade', '-c', 'config_examples/config_bittrex.example.json'])
|
||||
|
||||
assert log_has('Using config: config_bittrex.json.example ...', caplog)
|
||||
assert log_has('Using config: config_examples/config_bittrex.example.json ...', caplog)
|
||||
assert worker_mock.call_count == 4
|
||||
assert reconfigure_mock.call_count == 1
|
||||
assert isinstance(worker.freqtrade, FreqtradeBot)
|
||||
@ -180,7 +184,11 @@ def test_reconfigure(mocker, default_conf) -> None:
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.init_db', MagicMock())
|
||||
|
||||
args = Arguments(['trade', '-c', 'config_bittrex.json.example']).get_parsed_arg()
|
||||
args = Arguments([
|
||||
'trade',
|
||||
'-c',
|
||||
'config_examples/config_bittrex.example.json'
|
||||
]).get_parsed_arg()
|
||||
worker = Worker(args=args, config=default_conf)
|
||||
freqtrade = worker.freqtrade
|
||||
|
||||
|
@ -7,7 +7,7 @@ from unittest.mock import MagicMock
|
||||
import pytest
|
||||
|
||||
from freqtrade.misc import (decimals_per_coin, file_dump_json, file_load_json, format_ms_time,
|
||||
pair_to_filename, plural, render_template,
|
||||
pair_to_filename, parse_db_uri_for_logging, plural, render_template,
|
||||
render_template_with_fallback, round_coin_value, safe_value_fallback,
|
||||
safe_value_fallback2, shorten_date)
|
||||
|
||||
@ -179,3 +179,18 @@ def test_render_template_fallback(mocker):
|
||||
)
|
||||
assert isinstance(val, str)
|
||||
assert 'if self.dp' in val
|
||||
|
||||
|
||||
def test_parse_db_uri_for_logging() -> None:
|
||||
postgresql_conn_uri = "postgresql+psycopg2://scott123:scott123@host/dbname"
|
||||
mariadb_conn_uri = "mariadb+mariadbconnector://app_user:Password123!@127.0.0.1:3306/company"
|
||||
mysql_conn_uri = "mysql+pymysql://user:pass@some_mariadb/dbname?charset=utf8mb4"
|
||||
sqlite_conn_uri = "sqlite:////freqtrade/user_data/tradesv3.sqlite"
|
||||
censored_pwd = "*****"
|
||||
|
||||
def get_pwd(x): return x.split(':')[2].split('@')[0]
|
||||
|
||||
assert get_pwd(parse_db_uri_for_logging(postgresql_conn_uri)) == censored_pwd
|
||||
assert get_pwd(parse_db_uri_for_logging(mariadb_conn_uri)) == censored_pwd
|
||||
assert get_pwd(parse_db_uri_for_logging(mysql_conn_uri)) == censored_pwd
|
||||
assert sqlite_conn_uri == parse_db_uri_for_logging(sqlite_conn_uri)
|
||||
|
@ -364,7 +364,7 @@ def test_start_plot_dataframe(mocker):
|
||||
aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock())
|
||||
args = [
|
||||
"plot-dataframe",
|
||||
"--config", "config_bittrex.json.example",
|
||||
"--config", "config_examples/config_bittrex.example.json",
|
||||
"--pairs", "ETH/BTC"
|
||||
]
|
||||
start_plot_dataframe(get_args(args))
|
||||
@ -408,7 +408,7 @@ def test_start_plot_profit(mocker):
|
||||
aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock())
|
||||
args = [
|
||||
"plot-profit",
|
||||
"--config", "config_bittrex.json.example",
|
||||
"--config", "config_examples/config_bittrex.example.json",
|
||||
"--pairs", "ETH/BTC"
|
||||
]
|
||||
start_plot_profit(get_args(args))
|
||||
|
@ -121,13 +121,19 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None:
|
||||
freqtrade.wallets.get_trade_stake_amount('ETH/BTC')
|
||||
|
||||
|
||||
@pytest.mark.parametrize("balance_ratio,result1,result2", [
|
||||
(1, 50, 66.66666),
|
||||
(0.99, 49.5, 66.0),
|
||||
(0.50, 25, 33.3333),
|
||||
@pytest.mark.parametrize("balance_ratio,capital,result1,result2", [
|
||||
(1, None, 50, 66.66666),
|
||||
(0.99, None, 49.5, 66.0),
|
||||
(0.50, None, 25, 33.3333),
|
||||
# Tests with capital ignore balance_ratio
|
||||
(1, 100, 50, 0.0),
|
||||
(0.99, 200, 50, 66.66666),
|
||||
(0.99, 150, 50, 50),
|
||||
(0.50, 50, 25, 0.0),
|
||||
(0.50, 10, 5, 0.0),
|
||||
])
|
||||
def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_ratio, result1,
|
||||
result2, limit_buy_order_open,
|
||||
def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_ratio, capital,
|
||||
result1, result2, limit_buy_order_open,
|
||||
fee, mocker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@ -141,6 +147,8 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r
|
||||
conf['dry_run_wallet'] = 100
|
||||
conf['max_open_trades'] = 2
|
||||
conf['tradable_balance_ratio'] = balance_ratio
|
||||
if capital is not None:
|
||||
conf['available_capital'] = capital
|
||||
|
||||
freqtrade = get_patched_freqtradebot(mocker, conf)
|
||||
|
||||
@ -170,3 +178,49 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, balance_r
|
||||
freqtrade.config['max_open_trades'] = 0
|
||||
result = freqtrade.wallets.get_trade_stake_amount('NEO/USDT')
|
||||
assert result == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize('stake_amount,min_stake_amount,max_stake_amount,expected', [
|
||||
(22, 11, 50, 22),
|
||||
(100, 11, 500, 100),
|
||||
(1000, 11, 500, 500), # Above max-stake
|
||||
(20, 15, 10, 0), # Minimum stake > max-stake
|
||||
(1, 11, 100, 11), # Below min stake
|
||||
(1, 15, 10, 0), # Below min stake and min_stake > max_stake
|
||||
|
||||
])
|
||||
def test__validate_stake_amount(mocker, default_conf,
|
||||
stake_amount, min_stake_amount, max_stake_amount, expected):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
|
||||
mocker.patch("freqtrade.wallets.Wallets.get_available_stake_amount",
|
||||
return_value=max_stake_amount)
|
||||
res = freqtrade.wallets._validate_stake_amount('XRP/USDT', stake_amount, min_stake_amount)
|
||||
assert res == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('available_capital,closed_profit,open_stakes,free,expected', [
|
||||
(None, 10, 100, 910, 1000),
|
||||
(None, 0, 0, 2500, 2500),
|
||||
(None, 500, 0, 2500, 2000),
|
||||
(None, 500, 0, 2500, 2000),
|
||||
(None, -70, 0, 1930, 2000),
|
||||
# Only available balance matters when it's set.
|
||||
(100, 0, 0, 0, 100),
|
||||
(1000, 0, 2, 5, 1000),
|
||||
(1235, 2250, 2, 5, 1235),
|
||||
(1235, -2250, 2, 5, 1235),
|
||||
])
|
||||
def test_get_starting_balance(mocker, default_conf, available_capital, closed_profit,
|
||||
open_stakes, free, expected):
|
||||
if available_capital:
|
||||
default_conf['available_capital'] = available_capital
|
||||
mocker.patch("freqtrade.persistence.models.Trade.get_total_closed_profit",
|
||||
return_value=closed_profit)
|
||||
mocker.patch("freqtrade.persistence.models.Trade.total_open_trades_stakes",
|
||||
return_value=open_stakes)
|
||||
mocker.patch("freqtrade.wallets.Wallets.get_free", return_value=free)
|
||||
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
|
||||
assert freqtrade.wallets.get_starting_balance() == expected
|
||||
|
Loading…
Reference in New Issue
Block a user