161 lines
6.5 KiB
Python
161 lines
6.5 KiB
Python
import json
|
|
|
|
|
|
class Strategies:
|
|
def __init__(self, loaded_strats, trades):
|
|
self.strat_list = loaded_strats
|
|
self.trades = trades
|
|
|
|
def new_strategy(self, data):
|
|
self.strat_list.append(data)
|
|
|
|
def delete_strategy(self, name):
|
|
obj = self.get_strategy_by_name(name)
|
|
if obj:
|
|
self.strat_list.remove(obj)
|
|
|
|
def get_strategies(self):
|
|
return self.strat_list
|
|
|
|
def get_strategy_by_name(self, name):
|
|
for obj in self.strat_list:
|
|
if obj['name'] == name:
|
|
return obj
|
|
return False
|
|
|
|
def execute_cmd(self, strategy, action, cmd):
|
|
order_type = 'LIMIT'
|
|
if action == 'open_position':
|
|
# Attempt to create the trade.
|
|
trade_id = self.trades.new_trade(strategy['symbol'], cmd, order_type, strategy['trade_amount'])
|
|
# If the trade didn't fail.
|
|
if trade_id is not False:
|
|
# Set the active flag in strategy.
|
|
strategy['active'] = True
|
|
strategy['current_position'] += strategy['trade_amount']
|
|
strategy['trades'].append(trade_id)
|
|
return 'position_opened'
|
|
else:
|
|
print('Failed to place trade')
|
|
return 'failed'
|
|
|
|
if (action == 'stop_loss') or (action == 'take_profit'):
|
|
if action == 'stop_loss':
|
|
order_type = 'MARKET'
|
|
# Attempt to create the trade.
|
|
trade = self.trades.new_trade(strategy['symbol'], cmd, order_type, strategy['current_position'])
|
|
# If the trade didn't fail.
|
|
if trade is not False:
|
|
# Set the active flag in strategy.
|
|
strategy['active'] = False
|
|
strategy['current_position'] = 0
|
|
return 'position_closed'
|
|
else:
|
|
print('Failed to place trade')
|
|
return 'failed'
|
|
print('Strategies.execute_cmd: Invalid action received.')
|
|
return 'failed'
|
|
|
|
def update(self, signals):
|
|
"""
|
|
Receives a reference to updated signal data. Loops through all
|
|
published strategies and evaluates conditions against the data.
|
|
This function returns a list of strategies and action commands.
|
|
"""
|
|
# Object containing data to return to function caller.
|
|
actions = {}
|
|
# Loop through all the published strategies.
|
|
for strategy in self.strat_list:
|
|
# Process any take_profit strategy.
|
|
if strategy['type'] == 'take_profit':
|
|
action, cmd = self.eval_tp_stg(strategy, signals)
|
|
if action == 'do_nothing':
|
|
return False
|
|
else:
|
|
# Execute the command.
|
|
actions[strategy['name']] = self.execute_cmd(strategy, action, cmd)
|
|
else:
|
|
print(f"Strategy.update: Strategy of type {strategy['type']} - not yet implemented.")
|
|
if len(actions) != 0:
|
|
return actions
|
|
else:
|
|
return False
|
|
|
|
def eval_tp_stg(self, strategy, signals):
|
|
"""
|
|
:param strategy: str: The strategy to evaluate.
|
|
:param signals: Signals: A reference to an object that handles current signal states.
|
|
:return action: Action required based on evaluation. format{cmd:str, amount:real, margin:int}
|
|
"""
|
|
|
|
def condition_satisfied(sig_name, vlue):
|
|
signal = signals.get_signal_by_name(sig_name)
|
|
if vlue == json.dumps(signal.state):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def all_conditions_met(conditions):
|
|
if len(conditions) < 1:
|
|
print(f"no trade-in conditions supplied: {strategy['name']}")
|
|
return False
|
|
# Evaluate all conditions and return false if any are un-met.
|
|
for trigger_signal in conditions.keys():
|
|
trigger_value = conditions[trigger_signal]
|
|
# Compare this signal's state with the trigger_value
|
|
if not condition_satisfied(trigger_signal, trigger_value):
|
|
return False
|
|
return True
|
|
|
|
def trade_out_condition_met(condition_type):
|
|
if strategy[condition_type]['typ'] == 'conditional':
|
|
signal_name = strategy[condition_type]['trig']
|
|
signal_value = strategy[condition_type]['val']
|
|
if condition_satisfied(signal_name, signal_value):
|
|
# If the condition is met trade-out.
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
if strategy[condition_type]['typ'] != 'value':
|
|
raise ValueError('trade_out_condition_met: invalid condition_type')
|
|
if condition_type == 'take_profit':
|
|
# If the profit condition is met send command to take profit.
|
|
if strategy['gross_profit'] > strategy['take_profit']['val']:
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
# If the loss condition is met, return a trade-out command.
|
|
if strategy['gross_loss'] < strategy['stop_loss']['val']:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
trade_in_cmd = strategy['side']
|
|
if strategy['side'] == 'buy':
|
|
trade_out_cmd = 'sell'
|
|
else:
|
|
trade_out_cmd = 'buy'
|
|
|
|
# If trade-in conditions are met.
|
|
if all_conditions_met(strategy['trd_in_conds']):
|
|
# If the new trade wouldn't exceed max_position. Return a trade-in command.
|
|
proposed_position_size = strategy['current_position'] + strategy['trade_amount']
|
|
if proposed_position_size < strategy['max_position']:
|
|
return 'enter_position', trade_in_cmd
|
|
|
|
# If strategy is active test the take-profit or stop-loss conditions.
|
|
if strategy['active']:
|
|
# Conditional take-profit trades-out if a signals equals a set value.
|
|
if trade_out_condition_met('take_profit'):
|
|
return 'take_profit', trade_out_cmd
|
|
|
|
# Conditional stop-loss trades-outs if a signals value equals a set value.
|
|
if trade_out_condition_met('stop_loss'):
|
|
return 'stop_loss', trade_out_cmd
|
|
|
|
# No conditions were met.
|
|
print('Strategies were updated and nothing to do.')
|
|
return 'do_nothing', 'nothing'
|