Merge branch 'develop' into patch-1
This commit is contained in:
@@ -167,7 +167,7 @@ def reload_config(rpc: RPC = Depends(get_rpc)):
|
||||
|
||||
|
||||
@router.get('/pair_candles', response_model=PairHistory, tags=['candle data'])
|
||||
def pair_candles(pair: str, timeframe: str, limit: Optional[int], rpc=Depends(get_rpc)):
|
||||
def pair_candles(pair: str, timeframe: str, limit: Optional[int], rpc: RPC = Depends(get_rpc)):
|
||||
return rpc._rpc_analysed_dataframe(pair, timeframe, limit)
|
||||
|
||||
|
||||
|
31
freqtrade/rpc/api_server/ui/fallback_file.html
Normal file
31
freqtrade/rpc/api_server/ui/fallback_file.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Freqtrade UI</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #3c3c3c;
|
||||
color: #dedede;
|
||||
text-align: center;
|
||||
}
|
||||
.main-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 10rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-container">
|
||||
<h1>Freqtrade UI not installed.</h1>
|
||||
<p>Please run `freqtrade install-ui` in your terminal to install the UI files and restart your bot.</p>
|
||||
<p>You can then refresh this page and you should see the UI.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
freqtrade/rpc/api_server/ui/favicon.ico
Normal file
BIN
freqtrade/rpc/api_server/ui/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 124 KiB |
0
freqtrade/rpc/api_server/ui/installed/.gitkeep
Normal file
0
freqtrade/rpc/api_server/ui/installed/.gitkeep
Normal file
31
freqtrade/rpc/api_server/web_ui.py
Normal file
31
freqtrade/rpc/api_server/web_ui.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.exceptions import HTTPException
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
|
||||
router_ui = APIRouter()
|
||||
|
||||
|
||||
@router_ui.get('/favicon.ico', include_in_schema=False)
|
||||
async def favicon():
|
||||
return FileResponse(Path(__file__).parent / 'ui/favicon.ico')
|
||||
|
||||
|
||||
@router_ui.get('/{rest_of_path:path}', include_in_schema=False)
|
||||
async def index_html(rest_of_path: str):
|
||||
"""
|
||||
Emulate path fallback to index.html.
|
||||
"""
|
||||
if rest_of_path.startswith('api') or rest_of_path.startswith('.'):
|
||||
raise HTTPException(status_code=404, detail="Not Found")
|
||||
uibase = Path(__file__).parent / 'ui/installed/'
|
||||
if (uibase / rest_of_path).is_file():
|
||||
return FileResponse(str(uibase / rest_of_path))
|
||||
|
||||
index_file = uibase / 'index.html'
|
||||
if not index_file.is_file():
|
||||
return FileResponse(str(uibase.parent / 'fallback_file.html'))
|
||||
# Fall back to index.html, as indicated by vue router docs
|
||||
return FileResponse(str(index_file))
|
@@ -57,12 +57,16 @@ class ApiServer(RPCHandler):
|
||||
from freqtrade.rpc.api_server.api_auth import http_basic_or_jwt_token, router_login
|
||||
from freqtrade.rpc.api_server.api_v1 import router as api_v1
|
||||
from freqtrade.rpc.api_server.api_v1 import router_public as api_v1_public
|
||||
from freqtrade.rpc.api_server.web_ui import router_ui
|
||||
|
||||
app.include_router(api_v1_public, prefix="/api/v1")
|
||||
|
||||
app.include_router(api_v1, prefix="/api/v1",
|
||||
dependencies=[Depends(http_basic_or_jwt_token)],
|
||||
)
|
||||
app.include_router(router_login, prefix="/api/v1", tags=["auth"])
|
||||
# UI Router MUST be last!
|
||||
app.include_router(router_ui, prefix='')
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
|
@@ -9,7 +9,7 @@ from math import isnan
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import arrow
|
||||
from numpy import NAN, int64, mean
|
||||
from numpy import NAN, inf, int64, mean
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.configuration.timerange import TimeRange
|
||||
@@ -451,7 +451,7 @@ class RPC:
|
||||
pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency)
|
||||
rate = tickers.get(pair, {}).get('bid', None)
|
||||
if rate:
|
||||
if pair.startswith(stake_currency):
|
||||
if pair.startswith(stake_currency) and not pair.endswith(stake_currency):
|
||||
rate = 1.0 / rate
|
||||
est_stake = rate * balance.total
|
||||
except (ExchangeError):
|
||||
@@ -590,7 +590,8 @@ class RPC:
|
||||
raise RPCException(f'position for {pair} already open - id: {trade.id}')
|
||||
|
||||
# gen stake amount
|
||||
stakeamount = self._freqtrade.get_trade_stake_amount(pair)
|
||||
stakeamount = self._freqtrade.wallets.get_trade_stake_amount(
|
||||
pair, self._freqtrade.get_free_open_trades())
|
||||
|
||||
# execute buy
|
||||
if self._freqtrade.execute_buy(pair, stakeamount, price):
|
||||
@@ -746,6 +747,7 @@ class RPC:
|
||||
sell_mask = (dataframe['sell'] == 1)
|
||||
sell_signals = int(sell_mask.sum())
|
||||
dataframe.loc[sell_mask, '_sell_signal_open'] = dataframe.loc[sell_mask, 'open']
|
||||
dataframe = dataframe.replace([inf, -inf], NAN)
|
||||
dataframe = dataframe.replace({NAN: None})
|
||||
|
||||
res = {
|
||||
@@ -774,7 +776,8 @@ class RPC:
|
||||
})
|
||||
return res
|
||||
|
||||
def _rpc_analysed_dataframe(self, pair: str, timeframe: str, limit: int) -> Dict[str, Any]:
|
||||
def _rpc_analysed_dataframe(self, pair: str, timeframe: str,
|
||||
limit: Optional[int]) -> Dict[str, Any]:
|
||||
|
||||
_data, last_analyzed = self._freqtrade.dataprovider.get_analyzed_dataframe(
|
||||
pair, timeframe)
|
||||
|
@@ -18,6 +18,7 @@ from telegram.utils.helpers import escape_markdown
|
||||
|
||||
from freqtrade.__init__ import __version__
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import round_coin_value
|
||||
from freqtrade.rpc import RPC, RPCException, RPCHandler, RPCMessageType
|
||||
|
||||
|
||||
@@ -205,14 +206,14 @@ class Telegram(RPCHandler):
|
||||
else:
|
||||
msg['stake_amount_fiat'] = 0
|
||||
|
||||
message = ("\N{LARGE BLUE CIRCLE} *{exchange}:* Buying {pair}\n"
|
||||
"*Amount:* `{amount:.8f}`\n"
|
||||
"*Open Rate:* `{limit:.8f}`\n"
|
||||
"*Current Rate:* `{current_rate:.8f}`\n"
|
||||
"*Total:* `({stake_amount:.6f} {stake_currency}").format(**msg)
|
||||
message = (f"\N{LARGE BLUE CIRCLE} *{msg['exchange']}:* Buying {msg['pair']}\n"
|
||||
f"*Amount:* `{msg['amount']:.8f}`\n"
|
||||
f"*Open Rate:* `{msg['limit']:.8f}`\n"
|
||||
f"*Current Rate:* `{msg['current_rate']:.8f}`\n"
|
||||
f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}")
|
||||
|
||||
if msg.get('fiat_currency', None):
|
||||
message += ", {stake_amount_fiat:.3f} {fiat_currency}".format(**msg)
|
||||
message += f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}"
|
||||
message += ")`"
|
||||
|
||||
elif msg['type'] == RPCMessageType.BUY_CANCEL_NOTIFICATION:
|
||||
@@ -352,6 +353,7 @@ class Telegram(RPCHandler):
|
||||
try:
|
||||
statlist, head = self._rpc._rpc_status_table(
|
||||
self._config['stake_currency'], self._config.get('fiat_display_currency', ''))
|
||||
|
||||
message = tabulate(statlist, headers=head, tablefmt='simple')
|
||||
if(update.callback_query):
|
||||
query = update.callback_query
|
||||
@@ -384,7 +386,7 @@ class Telegram(RPCHandler):
|
||||
)
|
||||
stats_tab = tabulate(
|
||||
[[day['date'],
|
||||
f"{day['abs_profit']:.8f} {stats['stake_currency']}",
|
||||
f"{round_coin_value(day['abs_profit'], stats['stake_currency'])}",
|
||||
f"{day['fiat_value']:.3f} {stats['fiat_display_currency']}",
|
||||
f"{day['trade_count']} trades"] for day in stats['data']],
|
||||
headers=[
|
||||
@@ -438,18 +440,18 @@ class Telegram(RPCHandler):
|
||||
# Message to display
|
||||
if stats['closed_trade_count'] > 0:
|
||||
markdown_msg = ("*ROI:* Closed trades\n"
|
||||
f"∙ `{profit_closed_coin:.8f} {stake_cur} "
|
||||
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_fiat:.3f} {fiat_disp_cur}`\n")
|
||||
f"∙ `{round_coin_value(profit_closed_fiat, fiat_disp_cur)}`\n")
|
||||
else:
|
||||
markdown_msg = "`No closed trade` \n"
|
||||
|
||||
markdown_msg += (f"*ROI:* All trades\n"
|
||||
f"∙ `{profit_all_coin:.8f} {stake_cur} "
|
||||
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_fiat:.3f} {fiat_disp_cur}`\n"
|
||||
f"∙ `{round_coin_value(profit_all_fiat, fiat_disp_cur)}`\n"
|
||||
f"*Total Trade Count:* `{trade_count}`\n"
|
||||
f"*First Trade opened:* `{first_trade_date}`\n"
|
||||
f"*Latest Trade opened:* `{latest_trade_date}\n`"
|
||||
@@ -521,15 +523,17 @@ class Telegram(RPCHandler):
|
||||
"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"
|
||||
"\t`Available: {free: .8f}`\n"
|
||||
"\t`Balance: {balance: .8f}`\n"
|
||||
"\t`Pending: {used: .8f}`\n"
|
||||
"\t`Est. {stake}: {est_stake: .8f}`\n").format(**currency)
|
||||
for curr in result['currencies']:
|
||||
if curr['est_stake'] > 0.0001:
|
||||
curr_output = (
|
||||
f"*{curr['currency']}:*\n"
|
||||
f"\t`Available: {curr['free']:.8f}`\n"
|
||||
f"\t`Balance: {curr['balance']:.8f}`\n"
|
||||
f"\t`Pending: {curr['used']:.8f}`\n"
|
||||
f"\t`Est. {curr['stake']}: "
|
||||
f"{round_coin_value(curr['est_stake'], curr['stake'], False)}`\n")
|
||||
else:
|
||||
curr_output = "*{currency}:* not showing <1$ amount \n".format(**currency)
|
||||
curr_output = f"*{curr['currency']}:* not showing <1$ amount \n"
|
||||
|
||||
# Handle overflowing messsage length
|
||||
if len(output + curr_output) >= MAX_TELEGRAM_MESSAGE_LENGTH:
|
||||
@@ -539,8 +543,9 @@ class Telegram(RPCHandler):
|
||||
output += curr_output
|
||||
|
||||
output += ("\n*Estimated Value*:\n"
|
||||
"\t`{stake}: {total: .8f}`\n"
|
||||
"\t`{symbol}: {value: .2f}`\n").format(**result)
|
||||
f"\t`{result['stake']}: {result['total']: .8f}`\n"
|
||||
f"\t`{result['symbol']}: "
|
||||
f"{round_coin_value(result['value'], result['symbol'], False)}`\n")
|
||||
if(update.callback_query):
|
||||
query = update.callback_query
|
||||
self._update_msg(chat_id=query.message.chat_id, message_id=query.message.message_id, msg=output, callback_path="update_balance", reload_able=True)
|
||||
|
Reference in New Issue
Block a user