brighter-trading/Signals.py

159 lines
5.8 KiB
Python

from dataclasses import dataclass
@dataclass()
class Signal:
"""Class for individual signal properties and state."""
name: str
source1: str
prop1: str
source2: str
prop2: str
operator: str
state: bool = False
value1: int = None
value2: int = None
range: int = None
def set_value1(self, value):
self.value1 = value
def set_value2(self, value):
self.value2 = value
def compare(self):
if self.value1 is None:
raise ValueError('Signal: Cannot compare: value1 not set')
if self.value2 is None:
raise ValueError('Signal: Cannot compare: value2 not set')
previous_state = self.state
if self.operator == '+/-':
if self.range is None:
raise ValueError('Signal: Cannot compare: range not set')
if abs(self.value1 - self.value2) < self.range:
self.state = True
else:
self.state = False
else:
string = str(self.value1) + self.operator + str(self.value2)
print(string)
if eval(string):
self.state = True
else:
self.state = False
state_change = False
if self.state != previous_state:
state_change = True
return state_change
class Signals:
def __init__(self):
self.signals = []
# self.set_signals_defaults()
def set_signals_defaults(self):
"""These defaults are loaded if the config file is not found."""
sigs = self.get_signals_defaults()
for sig in sigs:
self.signals.append(Signal(name=sig['name'], source1=sig['source1'],
prop1=sig['prop1'], operator=sig['operator'],
source2=sig['source2'], prop2=sig['prop2'],
state=sig['state']))
return
@staticmethod
def get_signals_defaults():
"""These defaults are loaded if the config file is not found."""
s1 = {"name": "20x50_GC", "source1": "EMA 20",
"prop1": "value", "source2": "EMA 50",
"prop2": "value", "operator": ">",
"state": False, "value1": None,
"value2": None, "range": None}
s2 = {"name": "50x200_GC", "source1": "EMA 50",
"prop1": "value", "source2": "EMA 200",
"prop2": "value", "operator": ">",
"state": False, "value1": None,
"value2": None, "range": None}
s3 = {"name": "5x15_GC", "source1": "EMA 5",
"prop1": "value", "source2": "EMA 15",
"prop2": "value", "operator": ">",
"state": False, "value1": None,
"value2": None, "range": None}
return [s1, s2, s3]
def init_loaded_signals(self, signals_list):
for sig in signals_list:
self.signals.append(Signal(name=sig['name'], source1=sig['source1'],
prop1=sig['prop1'], operator=sig['operator'],
source2=sig['source2'], prop2=sig['prop2'],
state=sig['state']))
def get_signals(self):
return self.signals
def get_signal_by_name(self, name):
for signal in self.signals:
if signal.name == name:
return signal
return None
def new_signal(self, data):
self.signals.append(Signal(**data))
def delete_signal(self, signal_name):
print(f'removing {signal_name}')
for sig in self.signals:
if sig.name == signal_name:
self.signals.remove(sig)
break
def update_signals(self, candles, indicators):
# TODO: This function is not used. but may be used later if candles need to be specified.
for signal in self.signals:
self.process_signal(signal, candles, indicators)
def process_all_signals(self, indicators):
"""Loop through all the signals and process
them based on the last indicator results."""
state_changes = {}
for signal in self.signals:
change_in_state = self.process_signal(signal, indicators)
if change_in_state:
state_changes.update({signal.name: signal.state})
return state_changes
def process_signal(self, signal, indicators, candles=None):
"""Receives a signal, makes the comparison with the last value
calculated by the indicators and returns the result.
If candles are provided it will ask the indicators to
calculate new values based on those candles."""
if candles is None:
# Get the source of the first signal
source_1 = signal.source1
# Ask the indicator for the last result.
if source_1 in indicators.indicators:
signal.value1 = indicators.indicators[source_1].properties[signal.prop1]
else:
print('Could not calculate signal source indicator not found.')
return False
# Get the source of the second signal
source_2 = signal.source2
# If the source is a set value it will be stored in prop2
if source_2 == 'value':
signal.value2 = signal.prop2
else:
# Ask the indicator for the last result.
if source_2 in indicators.indicators:
signal.value2 = indicators.indicators[source_2].properties[signal.prop2]
else:
print('Could not calculate signal source2 indicator not found.')
return False
# Compare the retrieved values.
state_change = signal.compare()
return state_change