Skip to content

Commit

Permalink
Merge branch 'dev' into alpha
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Bouquet committed Jun 1, 2018
2 parents 035d9ff + 176f35f commit e1518ef
Show file tree
Hide file tree
Showing 17 changed files with 145 additions and 48 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# OctoBot [0.0.12-alpha](https://github.com/Drakkar-Software/OctoBot/tree/dev/docs/CHANGELOG.md)
# OctoBot [0.1.0-beta](https://github.com/Drakkar-Software/OctoBot/tree/dev/docs/CHANGELOG.md)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c83a127c42ba4a389ca86a92fba7c53c)](https://www.codacy.com/app/paul.bouquet/OctoBot?utm_source=github.com&utm_medium=referral&utm_content=Drakkar-Software/OctoBot&utm_campaign=Badge_Grade) [![Build Status](https://api.travis-ci.org/Drakkar-Software/OctoBot.svg?branch=dev)](https://travis-ci.org/Drakkar-Software/OctoBot) [![Coverage Status](https://coveralls.io/repos/github/Drakkar-Software/OctoBot/badge.svg?branch=dev)](https://coveralls.io/github/Drakkar-Software/OctoBot?branch=dev) [![Code Factor](https://www.codefactor.io/repository/github/Drakkar-Software/OctoBot/badge)](https://www.codefactor.io/repository/github/Drakkar-Software/OctoBot/overview/dev) [![Build Status](https://semaphoreci.com/api/v1/herklos/octobot/branches/dev/shields_badge.svg)](https://semaphoreci.com/herklos/octobot) [![Codefresh build status]( https://g.codefresh.io/api/badges/build?repoOwner=Drakkar-Software&repoName=OctoBot&branch=dev&pipelineName=OctoBot&accountName=herklos_marketplace&type=cf-1)]( https://g.codefresh.io/repositories/Drakkar-Software/OctoBot/builds?filter=trigger:build;branch:dev;service:5b06a377435197b088b1757a~OctoBot)
<p align="center">
<img src="../assets/octopus.svg" alt="Octobot Logo" height="400" width="400">
Expand Down
9 changes: 6 additions & 3 deletions config/cst.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from enum import Enum

SHORT_VERSION = "0.0.12"
REV_VERSION = "3"
VERSION_DEV_PHASE = "alpha"
SHORT_VERSION = "0.1.0"
REV_VERSION = "0"
VERSION_DEV_PHASE = "beta"
VERSION = "{0}-{1}".format(SHORT_VERSION, VERSION_DEV_PHASE)
LONG_VERSION = "{0}_{1}-{2}".format(SHORT_VERSION, REV_VERSION, VERSION_DEV_PHASE)

Expand Down Expand Up @@ -203,6 +203,9 @@ class TimeFrames(Enum):
ONE_MONTH = "1M"


MIN_EVAL_TIME_FRAME = TimeFrames.FIVE_MINUTES


TimeFramesMinutes = {
TimeFrames.ONE_MINUTE: 1,
TimeFrames.THREE_MINUTES: 3,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"refresh_rate_seconds" : 3600,
"refresh_rate_seconds" : 86400,
"relevant_history_months" : 3
}
14 changes: 13 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,23 @@ Changelog for 0.1.0-beta
- Config : "packages" root key renamed to "tentacles"

# Concerned issues :
#181 [Telegram] Pause and resume trading
#108 [RoadMap] format RoadMap into an attractive image
#109 [RoadMap] add RoadMap tracker on ReadMe.md
#136 [Tests] Improve trading tests coverage
#139 [Tests] Improve evaluator management tests coverage
#156 [Documentation] Add documentation for evaluator management classes
#163 [Exchanges Tests] implement web sockets for binance tests
#164 [ReadMe] make readme sexy !
#174 Renaming CryptoBot to Octobot
#181 [Telegram] Pause and resume trading
#183 Can't create order when order already on exchange on bot start
#186 [Twitter Interface] Some notifications are not sent to Twitter website

# New features :
- Telegram pause / resume trading
- Beautiful README and logo
- Create roadmap
- Improve tests coverage

# Bug fix :
- Fix negative portfolio in simulation
Expand Down
13 changes: 12 additions & 1 deletion docs/install/windows/windows_installer.bat
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ set TA_LIB_WIN64=https://github.com/Drakkar-Software/OctoBot/releases/download/0
set TWISTED_WIN32=https://github.com/Drakkar-Software/OctoBot/releases/download/0.0.12-alpha/Twisted-18.4.0-cp36-cp36m-win32.whl
set TWISTED_WIN64=https://github.com/Drakkar-Software/OctoBot/releases/download/0.0.12-alpha/Twisted-18.4.0-cp36-cp36m-win_amd64.whl

REM TEST INSTALL
%python_cmd% --version 2>NUL
if errorlevel 1 goto errorNoPython

REM REQUIREMENTS
echo **Installing dependencies...**
REM ARCH AMD64
Expand All @@ -37,4 +41,11 @@ copy default_evaluator_config.json evaluator_config.json
REM BOT MODULES INSTALL
echo **Installing modules...**
cd ..
%python_cmd% start.py -p install all
%python_cmd% start.py -p install all

REM Once done, exit the batch file -- skips executing the errorNoPython section
goto:eof

:errorNoPython
echo.
echo Error^: Python not installed (check https://github.com/Drakkar-Software/OctoBot/wiki/Installation)
1 change: 0 additions & 1 deletion docs/roadmap/.keep

This file was deleted.

Binary file removed docs/roadmap/v0.odp
Binary file not shown.
Binary file removed docs/roadmap/v0.pptx
Binary file not shown.
2 changes: 1 addition & 1 deletion evaluator/RealTime/realtime_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def _define_refresh_time(self):

def set_default_config(self):
time_frames = self.exchange.get_exchange_manager().get_config_time_frame()
min_time_frame = TimeFrameManager.find_min_time_frame(time_frames)
min_time_frame = TimeFrameManager.find_min_time_frame(time_frames, MIN_EVAL_TIME_FRAME)
refresh_rate = DEFAULT_WEBSOCKET_REAL_TIME_EVALUATOR_REFRESH_RATE_SECONDS if \
self.exchange.get_exchange_manager().websocket_available() \
else DEFAULT_REST_REAL_TIME_EVALUATOR_REFRESH_RATE_SECONDS
Expand Down
4 changes: 1 addition & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@
# TODO tests_require=[],
# TODO extras_require={},
classifiers=[
'Development Status :: 3 - Alpha',
'Development Status :: 4 - Beta',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX',
'Programming Language :: Python',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
],
Expand Down
2 changes: 1 addition & 1 deletion tests/static/evaluator_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"ADXMomentumEvaluator": true,


"InstantFluctuationsEvaluator": true,
"InstantFluctuationsEvaluator": false,


"TwitterNewsEvaluator": false,
Expand Down
22 changes: 12 additions & 10 deletions tests/unit_tests/evaluator_tests/test_evaluator_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from evaluator.evaluator_matrix import EvaluatorMatrix
from tests.test_utils.config import load_test_config
from config.cst import EvaluatorMatrixTypes
from config.cst import EvaluatorMatrixTypes, TimeFrames


def _get_tools():
Expand All @@ -21,13 +21,15 @@ def test_init():


def test_set_get_eval():
matrix_inst, matrix, config = _get_tools()
time_frames = [None, TimeFrames.ONE_HOUR]
values = [1, 1.02, math.pi, math.nan]
for value in values:
for matrix_type in EvaluatorMatrixTypes:
key = "{}{}".format(matrix_type, value)
matrix_inst.set_eval(matrix_type, key, value)
if math.isnan(value):
assert matrix_inst.get_eval_note(matrix, matrix_type, key) is None
else:
assert matrix_inst.get_eval_note(matrix, matrix_type, key) == value
for time_frame in time_frames:
matrix_inst, matrix, config = _get_tools()
for value in values:
for matrix_type in EvaluatorMatrixTypes:
key = "{}{}".format(matrix_type, value)
matrix_inst.set_eval(matrix_type, key, value, time_frame)
if math.isnan(value):
assert matrix_inst.get_eval_note(matrix, matrix_type, key, time_frame) is None
else:
assert matrix_inst.get_eval_note(matrix, matrix_type, key, time_frame) == value
51 changes: 51 additions & 0 deletions tests/unit_tests/evaluator_tests/test_symbol_evaluator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import ccxt

from trading.exchanges.exchange_manager import ExchangeManager
from evaluator.symbol_evaluator import SymbolEvaluator
from trading.trader.trader_simulator import TraderSimulator
from evaluator.cryptocurrency_evaluator import CryptocurrencyEvaluator
from evaluator.evaluator_creator import EvaluatorCreator
from tests.test_utils.config import load_test_config
from evaluator.Util.advanced_manager import AdvancedManager
from trading.trader.portfolio import Portfolio
from evaluator.Updaters.symbol_time_frames_updater import SymbolTimeFramesDataUpdaterThread
from evaluator.evaluator_threads_manager import EvaluatorThreadsManager
from config.cst import TimeFrames


def _get_tools():
symbol = "BTC/USDT"
exchange_traders = {}
exchange_traders2 = {}
config = load_test_config()
time_frame = TimeFrames.ONE_HOUR
AdvancedManager.create_class_list(config)
symbol_time_frame_updater_thread = SymbolTimeFramesDataUpdaterThread()
exchange_manager = ExchangeManager(config, ccxt.binance, is_simulated=True)
exchange_inst = exchange_manager.get_exchange()
trader_inst = TraderSimulator(config, exchange_inst, 0.3)
trader_inst.stop_order_manager()
trader_inst2 = TraderSimulator(config, exchange_inst, 0.3)
trader_inst2.stop_order_manager()
crypto_currency_evaluator = CryptocurrencyEvaluator(config, "Bitcoin", [])
symbol_evaluator = SymbolEvaluator(config, symbol, crypto_currency_evaluator)
exchange_traders[exchange_inst.get_name()] = trader_inst
exchange_traders2[exchange_inst.get_name()] = trader_inst2
symbol_evaluator.set_trader_simulators(exchange_traders)
symbol_evaluator.set_traders(exchange_traders2)
symbol_evaluator.strategies_eval_lists[exchange_inst.get_name()] = EvaluatorCreator.create_strategies_eval_list(
config)
evaluator_thread_manager = EvaluatorThreadsManager(config, symbol, time_frame, symbol_time_frame_updater_thread,
symbol_evaluator, exchange_inst, [])
trader_inst.portfolio.portfolio["USDT"] = {
Portfolio.TOTAL: 2000,
Portfolio.AVAILABLE: 2000
}
symbol_evaluator.add_evaluator_thread_manager(exchange_inst, symbol, time_frame, evaluator_thread_manager)
return symbol_evaluator, exchange_inst, time_frame, evaluator_thread_manager


def test_init():
symbol_evaluator, exchange_inst, time_frame, evaluator_thread_manager = _get_tools()
assert symbol_evaluator.evaluator_order_creator
assert symbol_evaluator.evaluator_thread_managers[exchange_inst.get_name()][time_frame] == evaluator_thread_manager
3 changes: 3 additions & 0 deletions tools/asynchronous_server.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import threading
import logging
from queue import Queue


Expand All @@ -22,5 +23,7 @@ def process_queue(self):
try:
while not self.queue.empty():
self.callback_method(*self.queue.get())
except Exception as e:
logging.getLogger(self.__class__.__name__).error("Error while processing queue: {0}".format(e))
finally:
self.is_computing = False
17 changes: 12 additions & 5 deletions tools/time_frame_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,19 @@ def get_previous_time_frame(config_time_frames, time_frame, origin_time_frame):
return origin_time_frame

@staticmethod
def find_min_time_frame(time_frames):
def find_min_time_frame(time_frames, min_time_frame=None):
tf_list = time_frames
if time_frames and isinstance(next(iter(time_frames)), TimeFrames):
tf_list = [t.value for t in time_frames]
min_index = 0
if min_time_frame:
min_index = TimeFrameManager.TimeFramesRank.index(min_time_frame)
# TimeFramesRank is the ordered list of timeframes
for tf in TimeFrameManager.TimeFramesRank:
if tf in time_frames:
for index, tf in enumerate(TimeFrameManager.TimeFramesRank):
tf_val = tf.value
if index >= min_index and tf_val in tf_list:
try:
return TimeFrames(tf)
return TimeFrames(tf_val)
except ValueError:
pass
return TimeFrames.ONE_MINUTE
return min_time_frame
2 changes: 1 addition & 1 deletion trading/exchanges/exchange_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def _set_config_time_frame(self):
if self.time_frame_exists(time_frame.value):
self.time_frames.append(time_frame)
# add shortest timeframe for realtime evaluators
client_shortest_time_frame = TimeFrameManager.find_min_time_frame(self.client_time_frames)
client_shortest_time_frame = TimeFrameManager.find_min_time_frame(self.client_time_frames, MIN_EVAL_TIME_FRAME)
if client_shortest_time_frame not in self.time_frames:
self.time_frames.append(client_shortest_time_frame)

Expand Down
49 changes: 30 additions & 19 deletions trading/trader/trader.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,39 +110,50 @@ def create_order_instance(self, order_type, symbol, current_price, quantity,
return order

def create_order(self, order, portfolio, loaded=False):
linked_to = None

new_order = order

# if this order is linked to another (ex : a sell limit order with a stop loss order)
if new_order.linked_to is not None:
new_order.linked_to.add_linked_order(new_order)
linked_to = new_order.linked_to

if not loaded:
if not self.simulate and not self.check_if_self_managed(order.get_order_type()):
new_order = self.exchange.create_order(order.get_order_type(),
order.get_order_symbol(),
order.get_origin_quantity(),
order.get_origin_price(),
order.origin_stop_price)
if not self.simulate and not self.check_if_self_managed(new_order.get_order_type()):
created_order = self.exchange.create_order(new_order.get_order_type(),
new_order.get_order_symbol(),
new_order.get_origin_quantity(),
new_order.get_origin_price(),
new_order.origin_stop_price)

# get real order from exchange
order = self.parse_exchange_order_to_order_instance(new_order)
new_order = self.parse_exchange_order_to_order_instance(created_order)

# rebind order notifier to new order instance
new_order.order_notifier = order.get_order_notifier()

# update the availability of the currency in the portfolio
portfolio.update_portfolio_available(order, is_new_order=True)
portfolio.update_portfolio_available(new_order, is_new_order=True)

title = "Order creation"
else:
title = "Order loaded"

self.logger.info("{0} : {1} | {2} | Price : {3} | Quantity : {4}".format(title,
order.get_order_symbol(),
order.get_order_type(),
order.get_origin_price(),
order.get_origin_quantity()))
new_order.get_order_symbol(),
new_order.get_order_type(),
new_order.get_origin_price(),
new_order.get_origin_quantity()))

# notify order manager of a new open order
self.order_manager.add_order_to_list(order)

# if this order is linked to another (ex : a sell limit order with a stop loss order)
if order.linked_to is not None:
order.linked_to.add_linked_order(order)
order.add_linked_order(order.linked_to)
self.order_manager.add_order_to_list(new_order)

return order
# if this order is linked to another
if linked_to is not None:
new_order.add_linked_order(linked_to)

return new_order

def cancel_order(self, order):
with order as odr:
Expand Down

0 comments on commit e1518ef

Please sign in to comment.