Skip to content

Commit

Permalink
Merge pull request #619 from Drakkar-Software/dev
Browse files Browse the repository at this point in the history
master merge
  • Loading branch information
GuillaumeDSM authored Nov 21, 2021
2 parents 4dc4f22 + ed59572 commit d8b1566
Show file tree
Hide file tree
Showing 40 changed files with 647 additions and 93 deletions.
2 changes: 1 addition & 1 deletion Evaluator/TA/trend_evaluator/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .trend import DoubleMovingAverageTrendEvaluator, EMADivergenceTrendEvaluator, DeathAndGoldenCrossEvaluator
from .trend import DoubleMovingAverageTrendEvaluator, EMADivergenceTrendEvaluator, DeathAndGoldenCrossEvaluator, SuperTrendEvaluator
4 changes: 4 additions & 0 deletions Evaluator/TA/trend_evaluator/config/SuperTrendEvaluator.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"factor": 3,
"length": 10
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"type": "object",
"title": "SuperTrendEvaluator",
"properties": {
"factor": {
"title": "Factor",
"type": "number",
"minimum": 0,
"default": 3
},
"length": {
"title": "Length",
"type": "number",
"multipleOf": 1,
"minimum": 1,
"default": 10
}
}
}
2 changes: 1 addition & 1 deletion Evaluator/TA/trend_evaluator/metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": "1.2.0",
"origin_package": "OctoBot-Default-Tentacles",
"tentacles": ["DoubleMovingAverageTrendEvaluator", "EMADivergenceTrendEvaluator", "DeathAndGoldenCrossEvaluator"],
"tentacles": ["DoubleMovingAverageTrendEvaluator", "EMADivergenceTrendEvaluator", "DeathAndGoldenCrossEvaluator", "SuperTrendEvaluator"],
"tentacles-requirements": []
}
2 changes: 2 additions & 0 deletions Evaluator/TA/trend_evaluator/resources/SuperTrendEvaluator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SuperTrendEvaluator is a trend-following indicator based on Average True Range [ATR](https://www.tradingview.com/scripts/averagetruerange/). The calculation of its single line combines trend detection and volatility. It can be used to detect changes in trend direction and to position stops.
Evaluates -1 if it's a uptrend else 1 if it's a downtrend.
88 changes: 78 additions & 10 deletions Evaluator/TA/trend_evaluator/trend.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,80 @@
import tentacles.Evaluator.Util as EvaluatorUtil


class SuperTrendEvaluator(evaluators.TAEvaluator):
FACTOR = "factor"
LENGTH = "length"
PREV_UPPER_BAND = "prev_upper_band"
PREV_LOWER_BAND = "prev_lower_band"
PREV_SUPERTREND = "prev_supertrend"
PREV_ATR = "prev_atr"

def __init__(self, tentacles_setup_config):
super().__init__(tentacles_setup_config)
self.config = tentacles_manager_api.get_tentacle_config(tentacles_setup_config, self.__class__)
self.factor = self.config.get(self.FACTOR, 3)
self.length = self.config.get(self.LENGTH, 10)
self.eval_note = commons_constants.START_PENDING_EVAL_NOTE
self.previous_value = {}

async def ohlcv_callback(self, exchange: str, exchange_id: str, cryptocurrency: str,
symbol: str, time_frame, candle, inc_in_construction_data):
exchange_symbol_data = self.get_exchange_symbol_data(exchange, exchange_id, symbol)
high = trading_api.get_symbol_high_candles(exchange_symbol_data, time_frame,
include_in_construction=inc_in_construction_data)
low = trading_api.get_symbol_low_candles(exchange_symbol_data, time_frame,
include_in_construction=inc_in_construction_data)
close = trading_api.get_symbol_close_candles(exchange_symbol_data, time_frame,
include_in_construction=inc_in_construction_data)
self.eval_note = commons_constants.START_PENDING_EVAL_NOTE
if len(close) > self.length:
await self.evaluate(cryptocurrency, symbol, time_frame, candle, high, low, close)
await self.evaluation_completed(cryptocurrency, symbol, time_frame,
eval_time=evaluators_util.get_eval_time(full_candle=candle,
time_frame=time_frame))

async def evaluate(self, cryptocurrency, symbol, time_frame, candle, high, low, close):
hl2 = EvaluatorUtil.CandlesUtil.HL2(high, low)[-1]
atr = tulipy.atr(high, low, close, self.length)[-1]

previous_value = self.get_previous_value(symbol, time_frame)

upper_band = hl2 + self.factor * atr
lower_band = hl2 - self.factor * atr
prev_upper_band = previous_value.get(self.PREV_UPPER_BAND, 0)
prev_lower_band = previous_value.get(self.PREV_LOWER_BAND, 0)

lower_band = lower_band if (lower_band > prev_lower_band or close[-2] < prev_lower_band) else prev_lower_band
upper_band = upper_band if (upper_band < prev_upper_band or close[-2] > prev_upper_band) else prev_upper_band

prev_super_trend = previous_value.get(self.PREV_SUPERTREND, 0)

if previous_value.get(self.PREV_ATR, None) is None:
self.eval_note = -1
elif prev_super_trend == prev_upper_band:
self.eval_note = 1 if close[-1] > upper_band else -1
else:
self.eval_note = -1 if close[-1] < lower_band else 1

previous_value[self.PREV_ATR] = atr
previous_value[self.PREV_UPPER_BAND] = upper_band
previous_value[self.PREV_LOWER_BAND] = lower_band
previous_value[self.PREV_SUPERTREND] = lower_band if self.eval_note == 1 else upper_band
return

def get_previous_value(self, symbol, time_frame):
try:
previous_symbol_value = self.previous_value[symbol]
except KeyError:
self.previous_value[symbol] = {}
previous_symbol_value = self.previous_value[symbol]
try:
return previous_symbol_value[time_frame]
except KeyError:
previous_symbol_value[time_frame] = {}
return previous_symbol_value[time_frame]


class DeathAndGoldenCrossEvaluator(evaluators.TAEvaluator):
FAST_LENGTH = "fast_length"
SLOW_LENGTH = "slow_length"
Expand All @@ -46,24 +120,19 @@ async def ohlcv_callback(self, exchange: str, exchange_id: str,
cryptocurrency: str, symbol: str, time_frame, candle, inc_in_construction_data):

close = trading_api.get_symbol_close_candles(self.get_exchange_symbol_data(exchange, exchange_id, symbol),
time_frame,
include_in_construction=inc_in_construction_data)
time_frame,
include_in_construction=inc_in_construction_data)
volume = trading_api.get_symbol_volume_candles(self.get_exchange_symbol_data(exchange, exchange_id, symbol),
time_frame,
include_in_construction=inc_in_construction_data)
time_frame,
include_in_construction=inc_in_construction_data)
self.eval_note = commons_constants.START_PENDING_EVAL_NOTE
if len(close) > self.slow_length:
await self.evaluate(cryptocurrency, symbol, time_frame, candle, close, volume)
await self.evaluation_completed(cryptocurrency, symbol, time_frame,
eval_time=evaluators_util.get_eval_time(full_candle=candle,
time_frame=time_frame))


async def evaluate(self, cryptocurrency, symbol, time_frame, candle, candle_data, volume_data):

ma1 = None
ma2 = None

if self.fast_ma_type == "vwma":
ma1 = tulipy.vwma(candle_data, volume_data, self.fast_length)[-1]
elif self.fast_ma_type == "lsma":
Expand All @@ -84,7 +153,6 @@ async def evaluate(self, cryptocurrency, symbol, time_frame, candle, candle_data
self.eval_note = 1



# evaluates position of the current (2 unit) average trend relatively to the 5 units average and 10 units average trend
class DoubleMovingAverageTrendEvaluator(evaluators.TAEvaluator):

Expand Down
8 changes: 6 additions & 2 deletions Services/Interfaces/web_interface/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
import abc
import flask
import os.path
from flask_caching import Cache

import tentacles.Services.Interfaces.web_interface.api as api
import octobot_commons.logging as bot_logging

server_instance = flask.Flask(__name__)
cache = Cache(config={"CACHE_TYPE": "SimpleCache"})


class Notifier:
Expand All @@ -47,8 +49,9 @@ def register_notifier(notification_key, notifier):
DATA_COLLECTOR_NOTIFICATION_KEY = "data_collector_notifications"
DASHBOARD_NOTIFICATION_KEY = "dashboard_notifications"

# Override system configuration content types
import tentacles.Services.Interfaces.web_interface.flask_util as flask_util

# Override system configuration content types
flask_util.init_content_types()
server_instance.json_encoder = flask_util.Jsonifier

Expand Down Expand Up @@ -128,9 +131,11 @@ def send_general_notifications(**kwargs):
def send_backtesting_status(**kwargs):
_send_notification(BACKTESTING_NOTIFICATION_KEY, **kwargs)


def send_data_collector_status(**kwargs):
_send_notification(DATA_COLLECTOR_NOTIFICATION_KEY, **kwargs)


def send_new_trade(dict_new_trade, is_simulated):
if is_simulated:
_send_notification(DASHBOARD_NOTIFICATION_KEY, simulated_trades=[dict_new_trade])
Expand Down Expand Up @@ -173,4 +178,3 @@ def get_errors_count():

def flush_errors_count():
bot_logging.reset_errors_count()

Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

<!-- Editable -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-editable.css', u=LAST_UPDATED_STATIC_FILES) }}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" integrity="sha384-KZO2FRYNmIHerhfYMjCIUaJeGBRXP7CN24SiNSG+wdDzgwvxWbl16wMVtWiJTcMt" crossorigin="anonymous"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" integrity="sha384-OXVF05DQEe311p6ohU11NwlnX08FzMCsyoXzGOaL+83dKAb3qS17yZJxESl8YrJQ" crossorigin="anonymous">

<!-- Own -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css', u=LAST_UPDATED_STATIC_FILES) }}">
Expand All @@ -60,7 +60,7 @@

<script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.min.js" integrity="sha384-XnTxmviuqUy3cHBf+lkYWuTSDlhxCDxd9RgSo5zvzsCq93P9xNa6eENuAITCwxNh" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap-select.min.js" integrity="sha384-SfMwgGnc3UiUUZF50PsPetXLqH2HSl/FmkMW/Ja3N2WaJ/fHLbCHPUsXzzrM6aet" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js" integrity="sha384-JnbsSLBmv2/R0fUmF2XYIcAEMPHEAO51Gitn9IjL4l89uFTIgtLF1+jqIqqd9FSk" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js" integrity="sha384-d3UHjPdzJkZuk5H3qKYMLRyWLAQBJbby2yr2Q58hXXtAGF8RSNO9jpLDlKKPv5v3" crossorigin="anonymous"></script>

<script src="{{ url_for('static', filename='js/lib/bootstrap-editable.min.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
<script src="{{ url_for('static', filename='js/common/cst.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,29 @@ <h1>
</h1>
</div>
<div class="card-body">
<div class="container-fluid row mx-0">
<div class="col-md-3 mb-3">
Evaluators :
<select class="mx-0 selectpicker" id="evaluatorsSelect" data-width="50%" data-window-padding="25" multiple>
</select>
</div>
<div class="col-md-3 mb-3">
Timeframes :
<select class="mx-0 selectpicker" id="timeframesSelect" data-width="50%" data-window-padding="25" multiple>
</select>
</div>
<div class="col-md-3 mb-3">
Symbols :
<select class="mx-0 selectpicker" id="symbolsSelect" data-width="50%" data-window-padding="25" multiple>
</select>
</div>
<div class="col-md-3 mb-3">
Exchanges :
<select class="mx-0 selectpicker" id="exchangesSelect" data-width="50%" data-window-padding="25" multiple>
</select>
</div>
</div>
<table class="table table-striped table-bordered table-sm table-hover table-responsive-lg" id="matrixDataTable">
<caption>Select data file(s) to use</caption>
<thead>
<tr>
<th scope="col">Evaluator</th>
Expand Down Expand Up @@ -53,5 +74,5 @@ <h1>
{% endblock %}

{% block additional_scripts %}
<script>$('#matrixDataTable').DataTable();</script>
<script src="{{ url_for('static', filename='js/components/advanced_matrix.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
{% endblock additional_scripts %}
10 changes: 9 additions & 1 deletion Services/Interfaces/web_interface/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@

api = flask.Blueprint('api', __name__, url_prefix='/api', template_folder="")

from tentacles.Services.Interfaces.web_interface.api import config
from tentacles.Services.Interfaces.web_interface.api import exchanges
from tentacles.Services.Interfaces.web_interface.api import metadata
from tentacles.Services.Interfaces.web_interface.api import trading
from tentacles.Services.Interfaces.web_interface.api import user_commands


from tentacles.Services.Interfaces.web_interface.api.config import (
get_config_currency,
set_config_currency,
change_reference_market_on_config_currencies,
)
from tentacles.Services.Interfaces.web_interface.api.exchanges import (
is_compatible_account,
)
Expand All @@ -42,7 +48,6 @@
user_command,
)


__all__ = [
"is_compatible_account",
"version",
Expand All @@ -53,4 +58,7 @@
"refresh_portfolio",
"currency_list",
"user_command",
"get_config_currency",
"set_config_currency",
"change_reference_market_on_config_currencies",
]
47 changes: 47 additions & 0 deletions Services/Interfaces/web_interface/api/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Drakkar-Software OctoBot-Interfaces
# Copyright (c) Drakkar-Software, All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.

import flask

import octobot_services.interfaces.util as interfaces_util
import tentacles.Services.Interfaces.web_interface.api as api
import tentacles.Services.Interfaces.web_interface.login as login
import tentacles.Services.Interfaces.web_interface.models as models
import tentacles.Services.Interfaces.web_interface.util as util


@api.api.route('/get_config_currency', methods=["GET"])
@login.login_required_when_activated
def get_config_currency():
return flask.jsonify(models.format_config_symbols(interfaces_util.get_edited_config()))


@api.api.route('/set_config_currency', methods=["POST"])
@login.login_required_when_activated
def set_config_currency():
request_data = flask.request.get_json()
success, reply = models.update_config_currencies(request_data["currencies"],
replace=(request_data.get("action", "update") == "replace"))
return util.get_rest_reply(flask.jsonify(reply)) if success else util.get_rest_reply(reply, 500)


@api.api.route('/change_reference_market_on_config_currencies', methods=["POST"])
@login.login_required_when_activated
def change_reference_market_on_config_currencies():
request_data = flask.request.get_json()
success, reply = models.change_reference_market_on_config_currencies(request_data["old_base_currency"],
request_data["new_base_currency"])
return util.get_rest_reply(flask.jsonify(reply)) if success else util.get_rest_reply(reply, 500)
3 changes: 2 additions & 1 deletion Services/Interfaces/web_interface/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@


# utility URLs
CURRENCIES_LIST_URL = "https://api.coingecko.com/api/v3/coins/list"
# top 250 sorted currencies (expects a page id at the end)
CURRENCIES_LIST_URL = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page="

# config keys
CONFIG_WATCHED_SYMBOLS = "watched_symbols"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
# Drakkar-Software OctoBot-Interfaces
# Copyright (c) Drakkar-Software, All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.

import mimetypes


Expand Down
4 changes: 4 additions & 0 deletions Services/Interfaces/web_interface/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@
REQUIREMENTS_KEY,
SYMBOL_KEY,
ID_KEY,
change_reference_market_on_config_currencies,
update_config_currencies,
)
from tentacles.Services.Interfaces.web_interface.models.dashboard import (
parse_get_symbol,
Expand Down Expand Up @@ -258,4 +260,6 @@
"get_symbols_values",
"get_full_candle_history_exchange_list",
"get_other_history_exchange_list",
"change_reference_market_on_config_currencies",
"update_config_currencies",
]
Loading

0 comments on commit d8b1566

Please sign in to comment.