diff --git a/Signals.py b/Signals.py index 086fdbd..01e8dc9 100644 --- a/Signals.py +++ b/Signals.py @@ -95,6 +95,12 @@ class Signals: 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)) diff --git a/Strategies.py b/Strategies.py index d67d6d3..846150b 100644 --- a/Strategies.py +++ b/Strategies.py @@ -1,15 +1,160 @@ +import json + + class Strategies: - def __init__(self, loaded_strats): + 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): - for obj in self.strat_list: - if obj['name'] == name: - self.strat_list.remove(obj) - break + 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' diff --git a/app.py b/app.py index f00aceb..a70abec 100644 --- a/app.py +++ b/app.py @@ -4,15 +4,17 @@ from flask import Flask, render_template, request, redirect, jsonify from flask_cors import cross_origin from binance.enums import * from flask_sock import Sock -# Handles all server side data -import data as bt -# *NOT DONE YET* -import trade + +# Handles all server side data and interactions. +from data import BrighterData # Define app app = Flask(__name__) sock = Sock(app) +# This object maintains all the application and historical data. +# Access to server, local storage, other classes go through here. +app_data = BrighterData() # app.config['SECRET_KEY'] = 'The quick brown fox jumps over the lazy dog' # app.config['CORS_HEADERS'] = 'Content-Type' @@ -21,8 +23,8 @@ sock = Sock(app) @app.route('/') def index(): # Passes data into an HTML template and serves it to a locally hosted server - rendered_data = bt.app_data.get_rendered_data() - js_data = bt.app_data.get_js_init_data() + rendered_data = app_data.get_rendered_data() + js_data = app_data.get_js_init_data() return render_template('index.html', title=rendered_data['title'], my_balances=rendered_data['my_balances'], @@ -43,7 +45,7 @@ def ws(sock): if msg_obj['message_type'] == 'candle_data': # Send the candle to the BrighterData_obj # and forward any returned data to the client. - r_data = bt.app_data.received_cdata(msg_obj['data']) + r_data = app_data.received_cdata(msg_obj['data']) if r_data: resp = { "reply": "updates", @@ -53,7 +55,7 @@ def ws(sock): if msg_obj['message_type'] == 'request': if msg_obj['data'] == 'signals': - signals = bt.app_data.get_signals() + signals = app_data.get_signals() if signals: resp = { "reply": "signals", @@ -62,7 +64,7 @@ def ws(sock): resp = json.dumps(resp) sock.send(resp) elif msg_obj['data'] == 'strategies': - strategies = bt.app_data.get_strategies() + strategies = app_data.get_strategies() if strategies: resp = { "reply": "strategies", @@ -75,10 +77,10 @@ def ws(sock): print(msg_obj['data']) if msg_obj['message_type'] == 'delete_signal': - bt.app_data.delete_signal(msg_obj['data']) + app_data.delete_signal(msg_obj['data']) if msg_obj['message_type'] == 'delete_strategy': - bt.app_data.delete_strategy(msg_obj['data']) + app_data.delete_strategy(msg_obj['data']) if msg_obj['message_type'] == 'reply': print(msg_obj['rep']) @@ -87,7 +89,7 @@ def ws(sock): if msg_obj['message_type'] == 'new_signal': # Send the data to the BrighterData_obj # and forward any returned data to the client. - r_data = bt.app_data.received_new_signal(msg_obj['data']) + r_data = app_data.received_new_signal(msg_obj['data']) if r_data: resp = { "reply": "signal_created", @@ -99,7 +101,7 @@ def ws(sock): if msg_obj['message_type'] == 'new_strategy': # Send the data to the BrighterData_obj # and forward any returned data to the client. - r_data = bt.app_data.received_new_strategy(msg_obj['data']) + r_data = app_data.received_new_strategy(msg_obj['data']) if r_data: resp = { "reply": "strategy_created", @@ -126,19 +128,20 @@ def ws(sock): @app.route('/buy', methods=['POST']) @cross_origin(origin='localhost', headers=['Content- Type', 'Authorization']) def buy(): - print(request.form) # Debug ****** - trade.order( - symbol=request.form['symbol'], side=SIDE_BUY, - type=ORDER_TYPE_MARKET, quantity=request.form['quantity']) + print('This buy route is currently not being used.') + # app_data.trades.new_trade( + # symbol=request.form['symbol'], side=SIDE_BUY, + # type=ORDER_TYPE_MARKET, quantity=request.form['quantity']) return redirect('/') @app.route('/sell', methods=['POST']) @cross_origin(origin='localhost', headers=['Content- Type', 'Authorization']) def sell(): - trade.order( - symbol=request.form['symbol'], side=SIDE_SELL, - type=ORDER_TYPE_MARKET, quantity=request.form['quantity']) + print('This sell route is currently not being used.') + # app_data.trades.new_trade( + # symbol=request.form['symbol'], side=SIDE_SELL, + # type=ORDER_TYPE_MARKET, quantity=request.form['quantity']) return redirect('/') @@ -148,10 +151,10 @@ def settings(): setting = request.form['setting'] if setting == 'interval': interval_state = request.form['timeframe'] - bt.app_data.config.chart_interval = interval_state + app_data.config.chart_interval = interval_state elif setting == 'trading_pair': trading_pair = request.form['trading_pair'] - bt.app_data.config.trading_pair = trading_pair + app_data.config.trading_pair = trading_pair elif setting == 'toggle_indicator': # Get a list of indicators to enable enabled_indicators = [] @@ -159,13 +162,13 @@ def settings(): if request.form[i] == 'indicator': enabled_indicators.append(i) # Set visibility for all indicators according to - for indctr in bt.app_data.indicators.indicator_list: + for indctr in app_data.indicators.indicator_list: if indctr in enabled_indicators: - bt.app_data.indicators.indicator_list[indctr]['visible'] = True + app_data.indicators.indicator_list[indctr]['visible'] = True else: - bt.app_data.indicators.indicator_list[indctr]['visible'] = False + app_data.indicators.indicator_list[indctr]['visible'] = False # Redirect without reloading history - bt.app_data.config.config_and_states('save') + app_data.config.config_and_states('save') return redirect('/') elif setting == 'edit_indicator': @@ -182,14 +185,14 @@ def settings(): if 'visible' not in attributes: attributes.update({'visible': False}) # Set the data in indicators according to - bt.app_data.indicators.indicator_list[indicator] = attributes + app_data.indicators.indicator_list[indicator] = attributes if 'delete' in request.form: indicator = request.form['delete'] # This will delete in both indicators and config. - bt.app_data.indicators.delete_indicator(indicator) + app_data.indicators.delete_indicator(indicator) # Redirect without reloading history - bt.app_data.config.config_and_states('save') + app_data.config.config_and_states('save') return redirect('/') elif setting == 'new_indicator': @@ -210,35 +213,35 @@ def settings(): value = int(value) properties[key] = value # Should create in indicators and update the list in config. - bt.app_data.indicators.create_indicator(name=indcr, itype=indtyp, properties=properties) + app_data.indicators.create_indicator(name=indcr, itype=indtyp, properties=properties) else: print('ERROR SETTING VALUE') print(f'The string received by the server was: /n{request.form}') - bt.app_data.config.config_and_states('save') - bt.app_data.candles.set_candle_history() + app_data.config.config_and_states('save') + app_data.candles.set_candle_history() return redirect('/') @app.route('/history') @cross_origin(origin='localhost', headers=['Content- Type', 'Authorization']) def history(): - symbol = bt.app_data.config.trading_pair - interval = bt.app_data.config.chart_interval - return jsonify(bt.app_data.candles.get_candle_history(symbol, interval, 1000)) + symbol = app_data.config.trading_pair + interval = app_data.config.chart_interval + return jsonify(app_data.candles.get_candle_history(symbol, interval, 1000)) @app.route('/saved_data') @cross_origin(origin='localhost', headers=['Content- Type', 'Authorization']) def saved_data(): - return jsonify(bt.app_data.indicators.indicator_list) + return jsonify(app_data.indicators.indicator_list) @app.route('/indicator_init') @cross_origin(origin='localhost', headers=['Content- Type', 'Authorization']) def indicator_init(): - symbol = bt.app_data.config.trading_pair - interval = bt.app_data.config.chart_interval - d = bt.app_data.indicators.get_indicator_data(symbol, interval, 800) + symbol = app_data.config.trading_pair + interval = app_data.config.chart_interval + d = app_data.indicators.get_indicator_data(symbol, interval, 800) return jsonify(d) diff --git a/data.py b/data.py index 4fdbcc6..914d854 100644 --- a/data.py +++ b/data.py @@ -7,6 +7,7 @@ from Configuration import Configuration from exchange_info import ExchangeInfo from indicators import Indicators from Signals import Signals +from trade import Trades import json @@ -38,7 +39,10 @@ class BrighterData: self.exchange_info = ExchangeInfo(self.client) # Object that maintains the strategies data - self.strategies = Strategies(self.config.strategies_list) + self.trades = Trades(self.client) + + # 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 @@ -80,10 +84,24 @@ class BrighterData: i_updates = self.indicators.update_indicators() # Process the signals based on the last indicator updates. state_changes = self.signals.process_all_signals(self.indicators) + # Update the trades instance. + trade_updates = self.trades.update(cdata) + # Update the strategies instance. + stg_updates = self.strategies.update(self.signals) + + # Format and return an update object. updates = {'i_updates': i_updates} if state_changes: print(state_changes) updates.update({'s_updates': state_changes}) + if stg_updates: + print(stg_updates) + updates.update({'stg_updts': stg_updates}) + + if trade_updates: + print(trade_updates) + updates.update({'trade_updts': trade_updates}) + return updates def received_new_signal(self, data): @@ -136,5 +154,3 @@ class BrighterData: # Delete the signal from the configuration file. self.config.remove('signals', signal_name) - -app_data = BrighterData() diff --git a/static/Alerts.js b/static/Alerts.js index 9e11881..a51080e 100644 --- a/static/Alerts.js +++ b/static/Alerts.js @@ -7,7 +7,12 @@ class Alert{ // Other info in the alert. this.state = state; // The alert messages. - this.msg = 'Signal state change: ' + this.source + ' = ' + this.state; + if (alert_type=='signal'){ + this.msg = 'Signal state change: ' + this.source + ' = ' + this.state; + } + if (alert_type=='strategy'){ + this.msg = 'Strategy alert: ' + this.source + ' = ' + this.state; + } } alert_source(){ return this.source; @@ -44,6 +49,14 @@ class Alerts { this.update_html(); } + if (alert_type == 'strategy'){ + // If the alert_type is strategy then data will + // contain a list of objects with format: { name: str, state: bool } + console.log('publishing strategy alerts') + this.alerts.push( new Alert('strategy', 'source', data) ); + this.update_html(); + + } } update_html(){ diff --git a/static/Strategies.js b/static/Strategies.js index 2e1401c..f27015a 100644 --- a/static/Strategies.js +++ b/static/Strategies.js @@ -61,6 +61,18 @@ class Strategies { strat.type = document.getElementById('strat_type').value; strat.side = document.getElementById('trade_in_side').value; strat.margin = document.getElementById('margin_select').value; + strat.trade_amount = document.getElementById('trade_amount').value; + strat.max_position = document.getElementById('strgy_total').value; + strat.trading_fee = document.getElementById('fee').value; + strat.max_loss = document.getElementById('max_loss').value; + strat.symbol = window.UI.data.trading_pair; + strat.net_profit = 0; + strat.gross_profit = 0; + strat.net_loss = 0; + strat.gross_loss = 0; + strat.current_position = 0; + strat.current_value = 0; + strat.active = false; strat.trd_in_conds = {}; let conds = Array.from(document.querySelectorAll('#trade_in_cond>li')); for (let cond of conds){ @@ -93,6 +105,15 @@ class Strategies { window.UI.data.comms.send_to_app( "new_strategy", strat); this.close_form(); } + update_received(stg_updts){ + if ( 'cmd' in stg_updts) { + let alert = + window.UI.alerts.publish_alerts('strategy', stg_updts); + this.executeCmd(stg_updts.cmd); + } + console.log('recieved stategy update.'); + console.log(stg_updts); + } set_data(strats){ for (let strat of strats){ // Add the strategy to the instance list. @@ -128,7 +149,7 @@ class Strategies { parent.appendChild(lbl); parent.appendChild(select); } - val_input(name, label, parent, min, max, i_val){ + val_input(name, label, parent, min, max, i_val, style = null){ /* Create an input element. name: name and id of the element. label: text displayed beside the element. @@ -144,8 +165,11 @@ class Strategies { input.id = name; input.type = 'number'; input.min = min; - input.max = max; + if (max) {input.max = max;} input.value = i_val; + if (style){ + input.style = style; + } parent.appendChild(lbl); parent.appendChild(input); @@ -170,12 +194,30 @@ class Strategies { // Clear previous content. this.clear_innerHTML(options); + // Add a horizontal rule. + options.appendChild(document.createElement('hr')); + //Create a drop down for buy/sell option this.dropDown('trade_in_side','Side:', options, ['buy','sell']); - //Create an input for margin trading value. (1-100) + //Create an input for the margin. (1-100) this.val_input('margin_select', 'Margin:', options, 1,100, 50); + // Add a line break. + options.appendChild( document.createElement('br') ); + + //Create an input for the amount. (1-*) + this.val_input('trade_amount', 'Trade amount:', options, 1, 0, 1,'width: 54px;'); + + //Create an input for the amount. (1-*) + this.val_input('strgy_total', 'Strategy total:', options, 1, 0, 10, 'width: 54px;'); + + // Add a line break. + options.appendChild( document.createElement('br') ); + + //Create an input for the fee. (0.01-1) + this.val_input('fee', 'Trading Fee:', options, 0.001, 1, 0.025); + // Create a un ordered list to hold the conditions of trading in. let ul_in_cond = document.createElement('ul'); ul_in_cond.id ='trade_in_cond'; @@ -251,6 +293,11 @@ class Strategies { // Add a horizontal rule. options.appendChild(document.createElement('hr')); + // Create an input for tolerable loss. + this.val_input('max_loss', 'Max loss :', options, 1,100, 50); + // Add a line break. + options.appendChild(document.createElement('br')); + //Create a drop down for Stop Loss type. this.dropDown('loss_typ','Stop-Loss type:', options, ['value', 'conditional']); // Add and onchange function to show and hide some options. @@ -268,7 +315,7 @@ class Strategies { // Add a line break. options.appendChild( document.createElement('br') ); - // Create an input for take profit value. + // Create an input for stop loss value. this.val_input('loss_val', 'Loss %:', options, 1,100, 50); // Set display to visible. this.hideShowTags(['loss_val_lbl','loss_val'], 'show'); diff --git a/static/communication.js b/static/communication.js index 6d42a4f..84e2267 100644 --- a/static/communication.js +++ b/static/communication.js @@ -80,6 +80,13 @@ class Comms { window.UI.signals.update_signal_states(updates); window.UI.alerts.publish_alerts('signal_changes', updates); } + if ('stg_updts' in msg.data){ + /* If the received message contains signal updates. + Forward them to the signals instance. These messages + only arrive if a signal state changes. */ + let stg_updts = msg.data['stg_updts']; + window.UI.strats.update_received(stg_updts); + } }else if (msg.reply == 'signals'){ /* On initialization the server will send a list of signals loaded from file. Forward this to the signals instance.*/ diff --git a/trade.py b/trade.py index 61a9207..12ff577 100644 --- a/trade.py +++ b/trade.py @@ -1,9 +1,200 @@ -import data as bt_data +import json +import requests +from datetime import datetime -def order(symbol, side, type, quantity): - try: - order = bt_data.client.create_order(symbol, side, type, quantity) - # Report error if order fails - except Exception as e: - flash(e.message, "error") +class Trade: + def __init__(self, symbol, side, order_type, position_size, price): + """ + :param symbol: The symbol of the trading pair. + :param side: BUY|SELL + :param order_type: LIMIT|MARKET + :param position_size: Value in USD + :param price: Asking price for LIMIT orders. + """ + # Returns a datetime object containing the local date and time + self.timestamp = datetime.now() + self.symbol = symbol + self.side = side + self.order_type = order_type + self.position_size = position_size + # Flag set once order is placed successfully. + self.order_placed = False + # Order info returned when order is placed. + self.order = None + # Flag set once order is filled + self.order_filled = False + # This flag is set when the trade is closed out. + self.trade_closed = False + # Opening value of the asset. + self.opening_price = price + # Current value + self.value = 0 + # Profit or Loss + self.profit_loss = 0 + # Profit or Loss in percentage. + self.pl_percentage = 0 + + def update(self, current_price): + # Utility function. + def percent(part, whole): + if whole == 0: + return 0 + pct = 100 * float(part) / float(whole) + return pct + + # Set the current value and profit/loss + initial_value = self.position_size * self.opening_price + self.value = self.position_size * current_price + if self.side == 'buy': + self.profit_loss = self.value - initial_value + else: + self.profit_loss = initial_value - self.value + self.pl_percentage = percent(self.profit_loss, initial_value) + + return self.profit_loss + + +class Trades: + def __init__(self, client): + """ + :param client: The socket connection to the exchange. + """ + # Socket connection to the exchange. + self.client = client + # For automating limit orders offset the current price by 1/100 percent. + self.offset_amount = 0.0001 + # Exchange fees. Maybe these can be fetched from the server? + self.exchange_fees = {'maker': 0.01, 'taker': 0.05} + # Hedge mode allows long and shorts to be placed simultaneously. + self.hedge_mode = False + # If hedge mode is disabled this is either {'buy','sell'}. + self.side = None + # A list of unfilled trades. + self.unfilled_trades = [] + # A list of filled trades. + self.filled_trades = [] + # A completed trades. + self.closed_trades = [] + # Number of open trades. + self.num_trades = 0 + # The quantity sum of all open trades. + self.total_position = 0 + # The value of all open trades in USD. + self.total_position_value = 0 + # Info on all futures symbols + self.info = client.futures_exchange_info() + # Dictionary of places the exchange requires after the decimal for all symbols. + self.symbols_n_precision = {} + for item in self.info['symbols']: + self.symbols_n_precision[item['symbol']] = item['quantityPrecision'] + + def update(self, cdata): + r_update = [] + # Check if any unfilled orders are now filled. + for trade in self.unfilled_trades: + if not trade.order_filled: + order = self.client.get_order(symbol=trade.symbol, orderId=trade.order['orderId']) + if order['status'] == 'FILLED': + trade.order_filled = True + self.filled_trades.append(trade) + r_update.append({trade.timestamp: 'filled'}) + # Delete filled trades from unfilled_trades + self.unfilled_trades[:] = (t for t in self.unfilled_trades if t.order_filled is False) + + for trade in self.filled_trades: + # Update the open trades. + ret = trade.update(cdata['close']) + r_update.append({trade.timestamp: {'pl': ret}}) + # If a trade has been closed... + if trade.trade_closed: + self.closed_trades.append(trade) + # Notify caller + r_update.append({trade.timestamp: 'closed'}) + # Delete closed trades from filled_trades + self.filled_trades[:] = (t for t in self.filled_trades if t.trade_closed is False) + + return r_update + + def new_trade(self, symbol, side, order_type, usd, price=None): + # If hedge mode is disabled return False if trade is on opposite side. + if self.hedge_mode is False: + if self.side is None: + self.side = side + if self.side != side: + return False + + # If no price is given, set the asking price to be offset by a small amount. + if price is None: + offset = self.get_price(symbol) * self.offset_amount + if side == 'buy': + price = f'-{offset}' + else: + price = f'+{offset}' + + # A relative limit order may be set by passing a string preceded with a +/- + if type(price) == str: + if ('+' in price) or ('-' in price): + price = self.get_price(symbol) + float(price) + else: + price = float(price) + + position_size = usd / price + # The required level of precision for this trading pair. + precision = self.symbols_n_precision[symbol] + # String representing the order amount formatted to the level of precision defined above. + order_amount = "{:0.0{}f}".format(position_size, precision) + # Create a trade and place the order. + trade = Trade(symbol, side, order_type, order_amount, price) + order = self.place_order(trade) + # If the order successfully placed store the trade. + if order: + # Set th order placed flag. + trade.order_placed = True + # Save the order info in the trade object. + trade.order = order + + # Update the trades instance. + self.num_trades += 1 + self.unfilled_trades.append(trade) + print(order) + return trade + else: + return False + + @staticmethod + def get_price(symbol): + # Todo: Make sure im getting the correct market price for futures. + request = requests.get(f'https://api.binance.com/api/v3/ticker/price?symbol={symbol}') + json_obj = json.loads(request.text) + return json_obj['price'] + + def place_order(self, trade): + if trade.order_type == 'MARKET': + try: + order = self.client.create_test_order(symbol=trade.symbol, + side=trade.side, + type=trade.order_type, + quantity=trade.position_size) + print('!!!Order created!!!') + return order + # Report error if order fails + except Exception as e: + print(e, "error") + return None + + elif trade.order_type == 'LIMIT': + try: + order = self.client.create_test_order( + symbol=trade.symbol, side=trade.side, type=trade.order_type, + timeInForce='GTC', quantity=trade.position_size, + price=trade.opening_price) + return order + # If order fails + except Exception as e: + print(e, "error") + return None + + else: + print(f'Trade: No Implementation for trade.order: {trade.order_type}') + return None