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'