207 lines
9.5 KiB
Python
207 lines
9.5 KiB
Python
from Strategies import Strategies
|
|
from candles import Candles
|
|
from Configuration import Configuration
|
|
from exchange import Exchange
|
|
from indicators import Indicators
|
|
from Signals import Signals
|
|
from trade import Trades
|
|
import json
|
|
|
|
|
|
class BrighterData:
|
|
def __init__(self):
|
|
|
|
# Object that interacts and maintains exchange and account data
|
|
self.exchange = Exchange()
|
|
|
|
# Configuration and settings for the user interface and charts
|
|
self.config = Configuration()
|
|
|
|
# Object that maintains signals. Initialize with any signals loaded from file.
|
|
self.signals = Signals(self.config.signals_list)
|
|
|
|
# Object that maintains candlestick and price data.
|
|
self.candles = Candles(self.config, self.exchange)
|
|
|
|
# Object that interacts with and maintains data from available indicators
|
|
self.indicators = Indicators(self.candles, self.config)
|
|
|
|
# Object that maintains the trades data
|
|
self.trades = Trades(self.config.trades)
|
|
# The Trades object needs to connect to an exchange.
|
|
self.trades.connect_exchange(exchange=self.exchange)
|
|
|
|
# Object that maintains the strategies data
|
|
self.strategies = Strategies(self.config.strategies_list, self.trades)
|
|
|
|
def get_js_init_data(self):
|
|
"""
|
|
Returns a JSON object of initialization data.
|
|
This is passed into the frontend HTML template for the javascript to access in the rendered HTML.
|
|
"""
|
|
js_data = {'i_types': self.indicators.indicator_types,
|
|
'indicators': self.indicators.indicator_list,
|
|
'interval': self.config.chart_interval,
|
|
'trading_pair': self.config.trading_pair}
|
|
return js_data
|
|
|
|
def get_rendered_data(self):
|
|
"""
|
|
Data to be injected into the HTML template that renders the frontend UI.
|
|
"""
|
|
rd = {}
|
|
rd['title'] = self.config.application_title # Title of the page
|
|
rd['my_balances'] = self.exchange.balances # Balances on the exchange
|
|
rd['symbols'] = self.exchange.symbols # Symbols information from the exchange
|
|
rd['intervals'] = self.exchange.intervals # Time candle time intervals available to stream
|
|
rd['chart_interval'] = self.config.chart_interval # The charts current interval setting
|
|
rd['indicator_types'] = self.indicators.indicator_types # All the types indicators Available
|
|
rd['indicator_list'] = self.indicators.get_indicator_list() # indicators available
|
|
rd['enabled_indicators'] = self.indicators.get_enabled_indicators() # list of indicators that are enabled
|
|
rd['ma_vals'] = self.indicators.bb_ma_val # A list of acceptable values to use with bolenger band creation
|
|
return rd
|
|
|
|
def received_cdata(self, cdata):
|
|
"""
|
|
This is called to pass in new price data when it is received.
|
|
:param cdata: <A candle object> - An object containing the most recent price data.
|
|
:return: <dict> - Dictionary object containing information about the updates to be passed onto the UI.
|
|
"""
|
|
# If this is the first candle received last_candle will be empty.
|
|
if not self.candles.last_candle:
|
|
# Set last_candle.
|
|
self.candles.last_candle = cdata
|
|
# If this is not the first candle, but it's the same as the last candle recorded.
|
|
elif cdata['time'] == self.candles.last_candle['time']:
|
|
# Return without doing anything.
|
|
return None
|
|
|
|
# A new candle is received with a different timestamp from the last candle.
|
|
# Update the instance data records.
|
|
self.candles.set_new_candle(cdata)
|
|
# Update the indicators and receive a dictionary of indicator results.
|
|
i_updates = self.indicators.update_indicators()
|
|
# Now that all the indicators have changed. Process the signals and receive a list of signals that
|
|
# have changed their states.
|
|
state_changes = self.signals.process_all_signals(self.indicators)
|
|
# Update the trades instance with the new price data.
|
|
trade_updates = self.trades.update(cdata)
|
|
# Update the strategies instance. Strategy execution is based on the signal states and trade values.
|
|
# This must be updated last.
|
|
stg_updates = self.strategies.update(self.signals)
|
|
|
|
# Format and return an update object to pass information to the frontend UI.
|
|
updates = {}
|
|
if i_updates:
|
|
updates.update({'i_updates': i_updates})
|
|
if state_changes:
|
|
print(state_changes)
|
|
updates.update({'s_updates': state_changes})
|
|
if trade_updates:
|
|
print(trade_updates)
|
|
updates.update({'trade_updts': trade_updates})
|
|
if stg_updates:
|
|
print(stg_updates)
|
|
updates.update({'stg_updts': stg_updates})
|
|
return updates
|
|
|
|
def received_new_signal(self, data):
|
|
"""
|
|
This is called when a new Signal has been defined and created in the UI.
|
|
|
|
:param data: <dict> - The attributes of the signal.
|
|
:return: An error if failed. On success return incoming data for chaining.
|
|
"""
|
|
# Validate the incoming data.
|
|
if 'name' not in data:
|
|
return 'data.py:received_new_signal() - The new signal has no name. '
|
|
# Forward the new signal data to the signals instance. So it can create a new signal.
|
|
self.signals.new_signal(data)
|
|
# Update config's list of signals and save it to file.
|
|
self.config.update_data('signals', self.signals.get_signals('dict'))
|
|
# Send the data back to where it came from.
|
|
return data
|
|
|
|
def received_new_strategy(self, data):
|
|
"""
|
|
This is called when a new Strategy has been defined and created in the UI.
|
|
|
|
:param data: <dict> - The attributes of the strategy.
|
|
:return: An error if failed. On success return incoming data for chaining.
|
|
"""
|
|
# Validate the incoming data.
|
|
if 'name' not in data:
|
|
return 'data.py:received_new_strategy() - The new strategy has no name. '
|
|
# Forward the new strategy data to the strategy's instance. So it can create a new strategy.
|
|
self.strategies.new_strategy(data)
|
|
# Update config's list of strategies and save to file.
|
|
self.config.update_data('strategies', self.strategies.get_strategies('dict'))
|
|
# Send the data back to where it came from.
|
|
return data
|
|
|
|
def delete_strategy(self, strategy_name):
|
|
# Delete the strategy from the strategies instance.
|
|
self.strategies.delete_strategy(strategy_name)
|
|
# Delete the strategy from the configuration file.
|
|
self.config.remove('strategies', strategy_name)
|
|
|
|
def get_signals(self):
|
|
"""Return a JSON object of all the signals in the signals instance."""
|
|
return self.signals.get_signals('json')
|
|
|
|
def get_strategies(self):
|
|
""" Return a JSON object of all the strategies in the strategies instance."""
|
|
return self.strategies.get_strategies('json')
|
|
|
|
def delete_signal(self, signal_name):
|
|
# Delete the signal from the signals instance.
|
|
self.signals.delete_signal(signal_name)
|
|
# Delete the signal from the configuration file.
|
|
self.config.remove('signals', signal_name)
|
|
|
|
def received_new_trade(self, data):
|
|
"""
|
|
This is called when a new trade has been defined and created in the UI.
|
|
Todo: Note - I handled this differently then signals and strategies. Is this better or not?
|
|
:param data: <dict> - The attributes of the trade.
|
|
:return: An error if failed. On success return incoming data to forward back to the UI.
|
|
"""
|
|
|
|
def vld(attr):
|
|
# Verify and validate input data.
|
|
if attr in data and data[attr] != '':
|
|
try:
|
|
return float(data[attr])
|
|
except ValueError:
|
|
return data[attr]
|
|
else:
|
|
return None
|
|
# Forward the request to trades.
|
|
status, result = self.trades.new_trade(target=vld('target'), symbol=vld('symbol'), price=vld('price'),
|
|
side=vld('side'), order_type=vld('orderType'),
|
|
qty=vld('quantity'))
|
|
# Log any error to the terminal.
|
|
if status == 'Error':
|
|
print(f'\napp.py:trade() - Error placing the trade: {result}')
|
|
# Log order to terminal.
|
|
print(f"\napp.py:trade() - Trade order received: target={vld('target')}, symbol={vld('symbol')},"
|
|
f" side={vld('side')}, type={vld('orderType')}, quantity={vld('quantity')},price={vld('price')}")
|
|
|
|
# Update config's list of trades and save to file.
|
|
self.config.update_data('trades', self.trades.get_trades('dict'))
|
|
# Send the data back to where it came from.
|
|
return data
|
|
|
|
# todo: This is how I handled this when I was using the flask interface to catch an html form submission.
|
|
# # Set the default symbol to the trading pair the UI is currently focused on.
|
|
# if symbol is None:
|
|
# symbol = self.config.trading_pair
|
|
# self.trades.new_trade(target=target, symbol=symbol, side=side, order_type=order_type,
|
|
# qty=quantity, price=price, offset=None)
|
|
# # Update config's list of trades and save to file.
|
|
# self.config.update_data('trades', self.trades.get_trades('dict'))
|
|
|
|
def get_trades(self):
|
|
""" Return a JSON object of all the trades in the trades instance."""
|
|
return self.trades.get_trades('json')
|