def compute_margin_quantity(self, trader, price): quantity = 0.0 if not trader.has_margin(self.instrument.market_id, self.instrument.trade_quantity, price): msg = "Not enought free margin %s, has %s but need %s" % ( self.instrument.quote, self.instrument.format_quantity(trader.account.margin_balance), self.instrument.format_quantity( self.instrument.trade_quantity)) logger.warning(msg) Terminal.inst().notice(msg, view='status') else: quantity = self.instrument.adjust_quantity( self.instrument.trade_quantity) return quantity
def execute(self, args): if not args: Terminal.inst().action("Missing parameters", view='status') return False if args[0] == 'traders': if len(args) == 1: self._trader_service.command(Trader.COMMAND_INFO, {}) return True elif len(args) == 2: self._trader_service.command(Trader.COMMAND_INFO, {'trader': args[1]}) return False elif len(args) == 3: self._trader_service.command(Trader.COMMAND_INFO, { 'trader': args[1], 'market-id': args[2] }) return False elif args[0] == 'apps': if len(args) == 1: self._strategy_service.command(Strategy.COMMAND_INFO, {}) return True elif len(args) == 2: self._strategy_service.command(Strategy.COMMAND_INFO, {'appliance': args[1]}) return True elif len(args) == 3: self._strategy_service.command(Strategy.COMMAND_INFO, { 'appliance': args[1], 'market-id': args[2] }) return True elif args[0] == 'notifiers': if len(args) == 1: self._notifier_service.command(Notifier.COMMAND_INFO, {}) return True elif len(args) == 2: self._notifier_service.command(Notifier.COMMAND_INFO, {'notifier': args[1]}) return True return False
def has_max_trades(self, max_trades): result = False if self.trades: self.lock() if len(self.trades) >= max_trades: # no more than max simultaneous trades result = True self.unlock() if result: msg = "Max trade reached for %s with %s" % (self.instrument.symbol, max_trades) logger.warning(msg) Terminal.inst().notice(msg, view='status') return result
def format(self, record): colors = self.colors( Terminal.inst().style() if Terminal.inst() else "") if record.levelno in (logging.ERROR, logging.CRITICAL) and self.use_color: record.name = colors['ERROR'] + '- ' + copy.copy( record.name) + colors["DEFAULT"] + ' ' record.levelname = colors['ERROR'] + copy.copy( record.levelname) + colors["DEFAULT"] + ' ' record.msg = colors['ERROR'] + copy.copy(str( record.msg)) + colors["DEFAULT"] return logging.Formatter.format(self, record) elif record.levelno == logging.WARNING and self.use_color: record.name = colors["WARNING"] + '- ' + copy.copy( record.name) + colors["DEFAULT"] + ' ' record.levelname = colors["WARNING"] + '- ' + copy.copy( record.levelname) + colors["DEFAULT"] + ' ' record.msg = colors["WARNING"] + copy.copy(str( record.msg)) + colors["DEFAULT"] return logging.Formatter.format(self, record) elif record.levelno == logging.INFO and self.use_color: record.name = '- ' record.levelname = colors["DEFAULT"] + '- ' + copy.copy( record.levelname) + colors["DEFAULT"] + ' ' record.msg = colors["DEFAULT"] + copy.copy(str( record.msg)) + colors["DEFAULT"] return logging.Formatter.format(self, record) elif record.levelno == logging.DEBUG and self.use_color: record.name = colors["NOTICE"] + '- ' + copy.copy( record.name) + colors["DEFAULT"] + ' ' record.levelname = colors["NOTICE"] + '- ' + copy.copy( record.levelname) + colors["DEFAULT"] + ' ' record.msg = colors["NOTICE"] + copy.copy(str( record.msg)) + colors["DEFAULT"] return logging.Formatter.format(self, record) else: return logging.Formatter.format(self, record)
def execute(self, args): if not args: return False appliance = None market_id = None trade_id = None action = 'stop-loss' stop_loss = 0.0 force = False # ie ":SL _ EURUSD 1 1.10" if len(args) < 4: Terminal.inst().action("Missing parameters", view='status') return False try: appliance, market_id = args[0], args[1] trade_id = int(args[2]) stop_loss = float(args[3]) if len(args) > 4: # create an order or modify the position, else use default force = str(args[4]) == "force" except Exception: Terminal.inst().action("Invalid parameters", view='status') return False self._strategy_service.command( Strategy.COMMAND_TRADE_MODIFY, { 'appliance': appliance, 'market-id': market_id, 'trade-id': trade_id, 'action': action, 'stop-loss': stop_loss, 'force': force }) return True
def execute(self, args): if not args: Terminal.inst().action("Missing parameters", view='status') return False appliance = None market_id = None trade_id = None action = "clean" # ie ":clean _ XRPUSDT 5" if len(args) != 3: Terminal.inst().action("Missing parameters", view='status') return False try: appliance, market_id = args[0], args[1] trade_id = int(args[2]) except Exception: Terminal.inst().action("Invalid parameters", view='status') return False self._strategy_service.command( Strategy.COMMAND_TRADE_CLEAN, { 'appliance': appliance, 'market-id': market_id, 'trade-id': trade_id, 'action': action }) return True
def execute(self, args): if not args: Terminal.inst().action("Missing parameters", view='status') return False # ie: ":chart altbtc BTCUSDT" appliance = None market_id = None # optionnal timeframe (could depend of the strategy) timeframe = None if len(args) < 2: Terminal.inst().action("Missing parameters", view='status') return False try: appliance, market_id = args[0], args[1] if len(args) == 3: timeframe = timeframe_from_str(args[2]) except Exception: Terminal.inst().action("Invalid parameters", view='status') return False self._strategy_service.command( Strategy.COMMAND_TRADER_CHART, { 'appliance': appliance, 'market-id': market_id, 'timeframe': timeframe, 'monitor-url': self._monitor_service.url() })
def execute(self, args): if not args: Terminal.inst().action("Missing parameters", view='status') return False appliance = None market_id = None action = 'del-region' operation_id = None # ie ":rmregion _ EURUSD 1" if len(args) < 3: Terminal.inst().action("Missing parameters", view='status') return False try: appliance, market_id = args[0], args[1] region_id = int(args[2]) except Exception: Terminal.inst().action("Invalid parameters", view='status') return False self._strategy_service.command( Strategy.COMMAND_TRADER_MODIFY, { 'appliance': appliance, 'market-id': market_id, 'action': action, 'region-id': region_id }) return True
def setup_live(self): super().setup_live() # pre-feed in live mode only Terminal.inst().info("In appliance %s retrieves last data history..." % self.name, view='status') now = datetime.now() # retrieve recent data history for market_id, instrument in self._instruments.items(): try: watcher = instrument.watcher(Watcher.WATCHER_PRICE_AND_VOLUME) if watcher: tfs = [(tf['timeframe'], tf['history']) for tf in self.timeframes_config.values() if tf['timeframe'] > 0] watcher.subscribe(instrument.symbol, tfs) # query for most recent candles for k, timeframe in self.timeframes_config.items(): if timeframe['timeframe'] > 0: l_from = now - timedelta( seconds=timeframe['history'] * timeframe['timeframe']) l_to = now watcher.historical_data(instrument.symbol, timeframe['timeframe'], from_date=l_from, to_date=l_to) # wait for this timeframe before processing instrument.want_timeframe(timeframe['timeframe']) except Exception as e: logger.error(repr(e)) logger.debug(traceback.format_exc()) Terminal.inst().info("Appliance data retrieved", view='status')
def execute(self, args): if not args: Terminal.inst().action("Missing parameters", view='status') return False if args[0] == 'traders': if len(args) == 1: self._trader_service.set_activity(True) Terminal.inst().action("Activated all traders", view='status') return True elif len(args) == 2: # @todo specific trader return False elif args[0] == 'apps': if len(args) == 1: self._strategy_service.set_activity(True) Terminal.inst().action( "Activated any markets for all appliances", view='status') return True elif len(args) == 2: appliance = self._strategy_service.appliance(args[1]) if appliance: appliance.set_activity(True) Terminal.inst().action( "Activated any markets for appliances %s" % args[1], view='status') return True elif len(args) == 3: appliance = self._strategy_service.appliance(args[1]) if appliance: appliance.set_activity(True, args[2]) Terminal.inst().action( "Activated instrument %s for appliances %s" % (args[2], args[1]), view='status') return True return False
def compute_asset_quantity(self, trader, price): quantity = 0.0 if trader.has_asset(self.instrument.quote): # quantity = min(quantity, trader.asset(self.instrument.quote).free) / self.instrument.market_ofr if trader.has_quantity(self.instrument.quote, self.instrument.trade_quantity): quantity = self.instrument.adjust_quantity(self.instrument.trade_quantity / price) # and adjusted to 0/max/step else: msg = "Not enought free quote asset %s, has %s but need %s" % ( self.instrument.quote, self.instrument.format_quantity(trader.asset(self.instrument.quote).free), self.instrument.format_quantity(self.instrument.trade_quantity)) logger.warning(msg) Terminal.inst().notice(msg, view='status') else: msg = "Quote asset %s not found" % self.instrument.quote logger.warning(msg) Terminal.inst().notice(msg, view='status') return quantity
def display_command_help(commands_handler, command_name): name, alias, details = commands_handler.get_command_help(command_name) if not name: Terminal.inst().message("Command %s not found" % command_name, view='content') return Terminal.inst().message("Details of the command %s" % name, view='content') if alias: Terminal.inst().message(" - Alias %s" % alias, view='content') if details: for entry in details: Terminal.inst().message(entry, view='content')
def display_welcome(): Terminal.inst().info("Console", view='content-head') Terminal.inst().action( "To type a command line, start with a ':', finally validate by <ENTER> or cancel with <ESC>.", view='content') Terminal.inst().action( "Enter command :help or :h for command details, and :quit :q to exit", view='content')
def checkout(self): var = { 'pretty': 'false', 'token': self.__apitoken, 'limit': 20, 'offset': 0 } # checkout social traders details and instantiates authors models watcher_config = self.service.watcher_config(self._name) for trader in watcher_config.get('authors'): trader_id = trader['id'] var['user_id'] = trader_id url = self._base_url + 'social/profile_trades.php?' + urllib.parse.urlencode( var) self._conn.request("GET", url) response = self._conn.getresponse() data = response.read() if response.status != 200: Terminal.inst().error( "Http error getting %s user %s trades !" % (self.name, trader_id)) continue data = json.loads(data) if data['error'] or data['warning']: Terminal.inst().error("API error getting %s user %s trades !" % (self.name, trader_id)) continue server_date = datetime.strptime(data['server_time'], "%Y-%m-%dT%H:%M:%S.%fZ") user_id = data['response']['user_id'] user_name = data['response']['username'] risk_score = int(data['response']['risk_score']) # author id stored as string but used as integer into queries author = Author(self, str(user_id), user_name) self._authors[str(user_id)] = author author.risk_score = risk_score author.success_rate = risk_score Terminal.inst().info("Found user %s id %s (risk:%i)" % (user_name, user_id, risk_score)) # @todo logger self.service.notify(Signal.SIGNAL_AUTHOR_ADDED, self.name, author) self._checkout = True
def table(self, columns, data): """ Draw a table in this view. """ if not columns or data is None: return self._table = (len(columns), len(data)) table = tabulate(data, headers=columns, tablefmt='psql', showindex=False, floatfmt=".2f", disable_numparse=True) # draw the table vt = Terminal.inst().view(self._id) if vt: vt.draw('', table, True)
def setup_live(self): super().setup_live() # pre-feed en live mode only Terminal.inst().info("> In appliance %s retrieves last data history..." % self.name, True) max_retry = 30 # retrieve recent data history for market_id, instrument in self._instruments.items(): try: price_and_vol_watcher = instrument.watcher(Watcher.WATCHER_PRICE_AND_VOLUME) if price_and_vol_watcher: # query for most recent candles price_and_vol_watcher.historical_data(market_id, Instrument.TF_MIN, n_last=720) # price_and_vol_watcher.historical_data(market_id, Instrument.TF_5MIN, n_last=120) # price_and_vol_watcher.historical_data(market_id, Instrument.TF_HOUR, n_last=4*24) # buy/sells signals # @todo except Exception as e: logger.error(repr(e)) logger.error(traceback.format_exc()) Terminal.inst().info("> Appliance data retrieved")
def on_key_pressed(self, key): super().on_key_pressed(key) if Terminal.inst().mode == Terminal.MODE_DEFAULT: if key == 'KEY_PPAGE': self.scroll_row(-(self.height()-4)) elif key == 'KEY_NPAGE': self.scroll_row(self.height()-4) elif (c == 'KEY_SR' or c == 'j'): self.scroll_row(-1) elif (c == 'KEY_SF' or c == 'k'): self.scroll_row(1) elif (c == 'KEY_SLEFT' or c == 'h'): self.scroll_col(-1) elif (c == 'KEY_SRIGHT' or c == 'l'): self.scroll_col(1)
def display_welcome(): Terminal.inst().info("Console", view='content-head') Terminal.inst().action( "To type a command line, start with a ':', finally validate by <ENTER> or cancel with <ESC>.", view='content') Terminal.inst().action( "Enter command :help or :h for command details, and :quit :q to exit", view='content') LOGO1 = """ SSSSSSSSSSSSSSS IIIIIIIIIIIIIIIIIIII SSSSSSSSSSSSSSS SS:::::::::::::::SI::::::::II::::::::I SS:::::::::::::::S S:::::SSSSSS::::::SI::::::::II::::::::IS:::::SSSSSS::::::S S:::::S SSSSSSSII::::::IIII::::::IIS:::::S SSSSSSS S:::::S I::::I I::::I S:::::S S:::::S I::::I I::::I S:::::S S::::SSSS I::::I I::::I S::::SSSS SS::::::SSSSS I::::I I::::I SS::::::SSSSS SSS::::::::SS I::::I I::::I SSS::::::::SS SSSSSS::::S I::::I I::::I SSSSSS::::S S:::::S I::::I I::::I S:::::S S:::::S I::::I I::::I S:::::S SSSSSSS S:::::SII::::::IIII::::::IISSSSSSS S:::::S S::::::SSSSSS:::::SI::::::::II::::::::IS::::::SSSSSS:::::S S:::::::::::::::SS I::::::::II::::::::IS:::::::::::::::SS SSSSSSSSSSSSSSS IIIIIIIIIIIIIIIIIIII SSSSSSSSSSSSSSS""" LOGO2 = """ ▄████████ ▄█ ▄█ ▄████████ ███ ███ ███ ███ ███ ███ ███ █▀ ███▌ ███▌ ███ █▀ ███ ███▌ ███▌ ███ ▀███████████ ███▌ ███▌ ▀███████████ ███ ███ ███ ███ ▄█ ███ ███ ███ ▄█ ███ ▄████████▀ █▀ █▀ ▄████████▀""" # ASCII art centered SIIS title logo = LOGO1 if randint(0, 10) < 5 else LOGO2 Terminal.inst().message(logo, view="content")
def table(self, columns, table, total_size=None): """ Draw a table in this view. """ if not columns or table is None: return self._table = total_size if total_size else (len(columns), len(table)) table_data = tabulate(table, headers=columns, tablefmt='psql', showindex=False, floatfmt=".2f", disable_numparse=True) # replace color espace code before drawing for k, v in Color.UTERM_COLORS_MAP.items(): table_data = table_data.replace(k, v) # draw the table vt = Terminal.inst().view(self._id) if vt: vt.draw('', table_data, True)
def execute(self, args): if not args: Terminal.inst().action("Missing parameters", view='status') return False appliance = None market_id = None trade_id = None action = "add-op" op = "dynamic-stop-loss" trigger = 0.0 stop_loss = 0.0 # ie ":DSL _ EURUSD 4 1.12 1.15" if len(args) != 5: Terminal.inst().action("Missing parameters", view='status') return False try: appliance, market_id = args[0], args[1] trade_id = int(args[2]) trigger = float(args[3]) stop_loss = float(args[4]) except Exception: Terminal.inst().action("Invalid parameters", view='status') return False self._strategy_service.command( Strategy.COMMAND_TRADE_MODIFY, { 'appliance': appliance, 'market-id': market_id, 'trade-id': trade_id, 'action': action, 'operation': op, 'trigger': trigger, 'stop-loss': stop_loss }) return True
def display_help_tools(): tools = Tool.find_tools() for tool in tools: try: alias, msgs = Tool.tool_help(tool) if alias: Terminal.inst().message( " --tool=%s, --%s %s" % (tool, alias, msgs[0] if len(msgs) > 0 else "")) else: Terminal.inst().message( " --tool=%s %s" % (tool, msgs[0] if len(msgs) > 0 else "")) for msg in msgs[1:]: Terminal.inst().message(" " + msg) except: pass
def command(self, command_type, data): if command_type == self.COMMAND_TOGGLE and data and data.get( "value", "") == "popup": self._popups = not self._popups Terminal.inst().action( "desktop notifier popups are now %s" % ("actives" if self._popups else "disabled", ), view='status') elif command_type == self.COMMAND_TOGGLE and data and data.get( "value", "") == "audible": self._audible = not self._audible Terminal.inst().action( "desktop notifier audio alertes are now %s" % ("actives" if self._audible else "disabled", ), view='status') elif command_type == self.COMMAND_INFO: Terminal.inst().info( "desktop notifier is %s" % ("active" if self._playpause else "disabled", ), view='content')
def execute(self, args): if not args: Terminal.inst().action("Missing parameters", view='status') return False appliance = None market_id = None region_id = None if len(args) >= 2: try: appliance, market_id = args[0], args[1] if len(args) >= 3: region_id = int(args[2]) else: region_id = -1 except Exception: Terminal.inst().action("Invalid parameters", view='status') return False self._strategy_service.command( Strategy.COMMAND_TRADER_INFO, { 'appliance': appliance, 'market-id': market_id, 'detail': 'region', 'region-id': region_id }) return True else: Terminal.inst().action("Missing or invalid parameters", view='status') return False return False
def execute(self, args): if not args: Terminal.inst().action("Missing parameters", view='status') return False appliance = None market_id = None trade_id = None action = 'take-profit' take_profit = 0.0 # ie ":TP _ EURUSD 1 1.15" if len(args) < 4: Terminal.inst().action("Missing parameters", view='status') return False try: appliance, market_id = args[0], args[1] trade_id = int(args[2]) take_profit = float(args[3]) except Exception: Terminal.inst().action("Invalid parameters", view='status') return False self._strategy_service.command( Strategy.COMMAND_TRADE_MODIFY, { 'appliance': appliance, 'market-id': market_id, 'trade-id': trade_id, 'action': action, 'take-profit': take_profit }) return True
def do_fetcher(options): Terminal.inst().info("Starting SIIS fetcher using %s identity..." % options['identity']) Terminal.inst().flush() # database manager Database.create(options) Database.inst().setup(options) watcher_service = WatcherService(options) fetcher = watcher_service.create_fetcher(options, options['broker']) timeframe = -1 cascaded = None if not options.get('timeframe'): timeframe = 60 # default to 1min else: if options['timeframe'] in TIMEFRAME_FROM_STR_MAP: timeframe = TIMEFRAME_FROM_STR_MAP[options['timeframe']] else: try: timeframe = int(options['timeframe']) except: pass if not options.get('cascaded'): cascaded = None else: if options['cascaded'] in TIMEFRAME_FROM_STR_MAP: cascaded = TIMEFRAME_FROM_STR_MAP[options['cascaded']] else: try: cascaded = int(options['cascaded']) except: pass if timeframe < 0: logger.error("Invalid timeframe") sys.exit(-1) try: fetcher.connect() except: sys.exit(-1) if fetcher.connected: logger.info("Fetcher authentified to %s, trying to collect data..." % fetcher.name) markets = fetcher.matching_symbols_set(options['market'].split(','), fetcher.available_instruments()) try: for market_id in markets: if not fetcher.has_instrument(market_id, options.get('spec')): logger.error("Market %s not found !" % (market_id, )) else: if options.get('install-market', False): fetcher.install_market(market_id) else: fetcher.fetch_and_generate(market_id, timeframe, options.get('from'), options.get('to'), options.get('last'), options.get('spec'), cascaded) except KeyboardInterrupt: pass finally: fetcher.disconnect() fetcher = None Terminal.inst().info("Flushing database...") Terminal.inst().flush() Database.terminate() Terminal.inst().info("Fetch done!") Terminal.inst().flush() Terminal.terminate() sys.exit(0)
def exec_indmargin_order(trader, order, market, open_exec_price, close_exec_price): """ Execute the order for indivisible margin position. @todo update to support only indivisible margin order """ current_position = None positions = [] trader.lock() if order.symbol: # in that case position is identifier by its market current_position = trader._positions.get(order.symbol) if current_position and current_position.is_opened(): # increase or reduce the current position org_quantity = current_position.quantity exec_price = 0.0 # # and adjust the position quantity (no possible hedging) # # price difference depending of the direction delta_price = 0 if current_position.direction == Position.LONG: delta_price = close_exec_price - current_position.entry_price # logger.debug("cl", delta_price, " use ", close_exec_price, " other ", open_exec_price, close_exec_price < open_exec_price) elif current_position.direction == Position.SHORT: delta_price = current_position.entry_price - close_exec_price # logger.debug("cs", delta_price, " use ", close_exec_price, " other ", open_exec_price, close_exec_price < open_exec_price) # keep for percent calculation prev_entry_price = current_position.entry_price or close_exec_price leverage = order.leverage # most of thoose data rarely change except the base_exchange_rate value_per_pip = market.value_per_pip contract_size = market.contract_size lot_size = market.lot_size one_pip_means = market.one_pip_means base_exchange_rate = market.base_exchange_rate margin_factor = market.margin_factor # logger.debug(order.symbol, bid_price, ofr_price, open_exec_price, close_exec_price, delta_price, current_position.entry_price, order.price) realized_position_cost = 0.0 # realized cost of the position in base currency # effective meaning of delta price in base currency effective_price = (delta_price / one_pip_means) * value_per_pip # in base currency position_gain_loss = 0.0 if order.direction == current_position.direction: # first, same direction, increase the position # it's what we have really buy realized_position_cost = order.quantity * (lot_size * contract_size ) # in base currency # check available margin margin_cost = realized_position_cost * margin_factor / base_exchange_rate if not trader.has_margin(margin_cost): # and then rejected order trader.unlock() trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_REJECTED, trader.name, (order.symbol, order.ref_order_id)) logger.error( "Not enought free margin for %s need %s but have %s!" % (order.symbol, margin_cost, trader.account.margin_balance)) return False # still in long, position size increase and adjust the entry price entry_price = ( (current_position.entry_price * current_position.quantity) + (open_exec_price * order.quantity)) / 2 current_position.entry_price = entry_price current_position.quantity += order.quantity # directly executed quantity order.executed = order.quantity exec_price = open_exec_price # increase used margin trader.account.add_used_margin(margin_cost) else: # different direction if current_position.quantity > order.quantity: # first case the direction still the same, reduce the position and the margin # take the profit/loss from the difference by order.quantity and adjust the entry price and quantity position_gain_loss = effective_price * order.quantity # it's what we have really closed realized_position_cost = order.quantity * ( lot_size * contract_size) # in base currency # and decrease used margin trader.account.add_used_margin(-realized_position_cost * margin_factor / base_exchange_rate) # entry price might not move... # current_position.entry_price = ((current_position.entry_price * current_position.quantity) - (close_exec_price * order.quantity)) / 2 current_position.quantity -= order.quantity exec_price = close_exec_price # directly executed quantity order.executed = order.quantity elif current_position.quantity == order.quantity: # second case the position is closed, exact quantity in the opposite direction position_gain_loss = effective_price * current_position.quantity current_position.quantity = 0.0 # it's what we have really closed realized_position_cost = order.quantity * ( lot_size * contract_size) # in base currency # directly executed quantity order.executed = order.quantity exec_price = close_exec_price # and decrease used margin trader.account.add_used_margin(-realized_position_cost * margin_factor / base_exchange_rate) else: # third case the position is reversed # 1) get the profit loss position_gain_loss = effective_price * current_position.quantity # it's what we have really closed realized_position_cost = order.quantity * ( lot_size * contract_size) # in base currency # first decrease of released margin trader.account.add_used_margin(-realized_position_cost * margin_factor / base_exchange_rate) # 2) adjust the position entry current_position.quantity = order.quantity - current_position.quantity current_position.entry_price = open_exec_price # 3) the direction is now at opposite current_position.direction = order.direction # directly executed quantity order.executed = order.quantity exec_price = open_exec_price # next increase margin of the new volume trader.account.add_used_margin( (order.quantity * lot_size * contract_size * margin_factor) / base_exchange_rate) # transaction time is current timestamp order.transact_time = trader.timestamp #order.set_position_id(current_position.position_id) if position_gain_loss != 0.0 and realized_position_cost > 0.0: # ratio gain_loss_rate = position_gain_loss / realized_position_cost relative_gain_loss_rate = delta_price / prev_entry_price # if maker close (limit+post-order) (for now same as market) current_position.profit_loss = position_gain_loss current_position.profit_loss_rate = gain_loss_rate # if taker close (market) current_position.profit_loss_market = position_gain_loss current_position.profit_loss_market_rate = gain_loss_rate trader.account.add_realized_profit_loss(position_gain_loss / base_exchange_rate) # display only for debug if position_gain_loss > 0.0: Terminal.inst().high( "Close profitable position with %.2f on %s (%.2fpips) (%.2f%%) at %s" % (position_gain_loss, order.symbol, delta_price / one_pip_means, gain_loss_rate * 100.0, market.format_price(close_exec_price)), view='debug') elif position_gain_loss < 0.0: Terminal.inst().low( "Close loosing position with %.2f on %s (%.2fpips) (%.2f%%) at %s" % (position_gain_loss, order.symbol, delta_price / one_pip_means, gain_loss_rate * 100.0, market.format_price(close_exec_price)), view='debug') logger.debug( "Account balance %.2f / Margin balance %.2f" % (trader.account.balance, trader.account.margin_balance)) else: gain_loss_rate = 0.0 # # history # # and keep for history (backtesting reporting) history = PaperTraderHistoryEntry( order, trader.account.balance, trader.account.margin_balance, delta_price / one_pip_means, gain_loss_rate, position_gain_loss, position_gain_loss / base_exchange_rate) trader._history.add(history) # unlock before notify signals trader.unlock() result = True # # order signal (SIGNAL_ORDER_OPENED+DELETED because we assume fully completed) # order_data = { 'id': order.order_id, 'symbol': order.symbol, 'type': order.order_type, 'direction': order.direction, 'timestamp': order.created_time, 'quantity': order.quantity, 'price': order.price, 'stop-price': order.stop_price, 'stop-loss': order.stop_loss, 'take-profit': order.take_profit, 'time-in-force': order.time_in_force } # signal as watcher service (opened + fully traded qty) trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_OPENED, trader.name, (order.symbol, order_data, order.ref_order_id)) order_data = { 'id': order.order_id, 'symbol': order.symbol, 'type': order.order_type, 'trade-id': 0, 'direction': order.direction, 'timestamp': order.transact_time, 'quantity': order.quantity, 'price': order.price, 'stop-price': order.stop_price, 'exec-price': exec_price, 'avg-price': exec_price, # current_position.entry_price, 'filled': order.executed, 'cumulative-filled': order.executed, 'quote-transacted': realized_position_cost, # its margin 'stop-loss': order.stop_loss, 'take-profit': order.take_profit, 'time-in-force': order.time_in_force, 'commission-amount': 0, # @todo 'commission-asset': trader.account.currency } trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_TRADED, trader.name, (order.symbol, order_data, order.ref_order_id)) # # position signal # # signal as watcher service if current_position.quantity <= 0: # closed position position_data = { 'id': current_position.position_id, 'symbol': current_position.symbol, 'direction': current_position.direction, 'timestamp': order.transact_time, 'quantity': 0, 'exec-price': exec_price, 'stop-loss': None, 'take-profit': None } trader.service.watcher_service.notify( Signal.SIGNAL_POSITION_DELETED, trader.name, (order.symbol, position_data, order.ref_order_id)) else: # updated position position_data = { 'id': current_position.position_id, 'symbol': current_position.symbol, 'direction': current_position.direction, 'timestamp': order.transact_time, 'quantity': current_position.quantity, # 'avg-price': current_position.entry_price, 'exec-price': exec_price, 'stop-loss': current_position.stop_loss, 'take-profit': current_position.take_profit, # 'profit-loss': @todo here } trader.service.watcher_service.notify( Signal.SIGNAL_POSITION_UPDATED, trader.name, (order.symbol, position_data, order.ref_order_id)) # and then deleted order trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_DELETED, trader.name, (order.symbol, order.order_id, "")) # if position is empty -> closed -> delete it if current_position.quantity <= 0.0: # take care this does not make an issue current_position.exit(None) # dont a next update # trader.lock() # if current_position.symbol in trader._positions: # del trader._positions[current_position.symbol] # trader.unlock() else: # unique position per market position_id = market.market_id # it's what we have really buy realized_position_cost = order.quantity * ( market.lot_size * market.contract_size) # in base currency # check available margin margin_cost = realized_position_cost * market.margin_factor / market.base_exchange_rate if not trader.has_margin(margin_cost): # and then rejected order trader.unlock() trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_REJECTED, trader.name, (order.symbol, order.ref_order_id)) logger.error( "Not enought free margin for %s need %s but have %s!" % (order.symbol, margin_cost, trader.account.margin_balance)) return False # create a new position at market position = Position(trader) position.symbol = order.symbol position.set_position_id(position_id) position.set_key(trader.service.gen_key()) position.entry(order.direction, order.symbol, order.quantity) position.leverage = order.leverage position.created_time = trader.timestamp account_currency = trader.account.currency # long are open on ofr and short on bid exec_price = open_exec_price position.entry_price = exec_price # logger.debug("%s %f %f %f %i" % ("el" if position.direction>0 else "es", position.entry_price, market.bid, market.ofr, market.bid < market.ofr)) # transaction time is creation position date time order.transact_time = position.created_time order.set_position_id(position_id) # directly executed quantity order.executed = order.quantity trader._positions[position_id] = position # increase used margin trader.account.add_used_margin(margin_cost) # # history # history = PaperTraderHistoryEntry(order, trader.account.balance, trader.account.margin_balance) trader._history.add(history) # unlock before notify signals trader.unlock() result = True # # order signal (SIGNAL_ORDER_OPENED+TRADED+DELETED, fully completed) # order_data = { 'id': order.order_id, 'symbol': order.symbol, 'type': order.order_type, 'direction': order.direction, 'timestamp': order.created_time, 'quantity': order.quantity, 'price': order.price, 'stop-price': order.stop_price, 'stop-loss': order.stop_loss, 'take-profit': order.take_profit, 'time-in-force': order.time_in_force } # signal as watcher service (opened + fully traded qty) trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_OPENED, trader.name, (order.symbol, order_data, order.ref_order_id)) order_data = { 'id': order.order_id, 'symbol': order.symbol, 'type': order.order_type, 'trade-id': 0, 'direction': order.direction, 'timestamp': order.transact_time, 'quantity': order.quantity, 'price': order.price, 'stop-price': order.stop_price, 'exec-price': position.entry_price, 'avg-price': position.entry_price, 'filled': order.executed, 'cumulative-filled': order.executed, 'quote-transacted': realized_position_cost, # its margin 'stop-loss': order.stop_loss, 'take-profit': order.take_profit, 'time-in-force': order.time_in_force, 'commission-amount': 0, # @todo 'commission-asset': trader.account.currency } #logger.info("%s %s %s" % (position.entry_price, position.quantity, order.direction)) trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_TRADED, trader.name, (order.symbol, order_data, order.ref_order_id)) # # position signal # position_data = { 'id': position.position_id, 'symbol': position.symbol, 'direction': position.direction, 'timestamp': order.transact_time, 'quantity': position.quantity, 'exec-price': position.entry_price, 'stop-loss': position.stop_loss, 'take-profit': position.take_profit } # signal as watcher service (position opened fully completed) trader.service.watcher_service.notify( Signal.SIGNAL_POSITION_OPENED, trader.name, (order.symbol, position_data, order.ref_order_id)) # and then deleted order trader.service.watcher_service.notify( Signal.SIGNAL_ORDER_DELETED, trader.name, (order.symbol, order.order_id, "")) return result
def emit(self, record): msg = self.format(record) if record.levelno == logging.ERROR: Terminal.inst().error(str(msg), view='default') Terminal.inst().error(str(msg), view='debug') elif record.levelno == logging.WARNING: Terminal.inst().warning(str(msg), view='default') Terminal.inst().error(str(msg), view='debug') elif record.levelno == logging.INFO: Terminal.inst().info(str(msg), view='default') Terminal.inst().info(str(msg), view='content') elif record.levelno == logging.DEBUG: Terminal.inst().message(str(msg), view='debug') else: Terminal.inst().message(str(msg), view='default')
def update(self): if not super().update(): return if not self._connected: # try reconnect time.sleep(10) self.connect() return if not self._checkout: # need checkout performed before update self.checkout() if not self._checkout: return self.lock() # update position of followed authors/traders for author_id, author in self._authors.items(): var = { 'pretty': 'false', 'token': self.__apitoken, 'limit': 20, 'offset': 0, 'user_id': int(author_id) } # https://1broker.com/api/v2/social/profile_trades.php?token=Aa508b7a7a5ffba14908bded38a88ee8&pretty=true&user_id=71368&limit=10&offset=0 url = self._base_url + 'social/profile_trades.php?' + urllib.parse.urlencode( var) self._conn.request("GET", url) response = self._conn.getresponse() data = response.read() if response.status != 200: Terminal.inst().error( "Http error getting %s user %s trades !" % (self.name, author_id)) continue data = json.loads(data) if data['error'] or data['warning']: Terminal.inst().error("API Error getting %s user %s trades !" % (self.name, author_id)) continue try: server_date = datetime.strptime(data['server_time'], "%Y-%m-%dT%H:%M:%S.%fZ") except: server_date = datetime.strptime(data['server_time'], "%Y-%m-%dT%H:%M:%SZ") open_trades = data['response']['trading_ideas_open'] closed_trades = data['response']['trading_ideas_closed'] for t in open_trades: position_id = t['position_id'] position = Position(self, position_id, author) direction = Position.LONG if t[ 'direction'] == 'long' else Position.SHORT date_created = datetime.strptime( t['date_created'], "%Y-%m-%dT%H:%M:%SZ") # .%fZ") is_open = t['is_open'] if not is_open: # should be open... continue symbol = t['symbol'] profit_loss_percent = float(t['profit_loss_percent']) comment_count = t['comment_count'] # @todo use as indicateur leverage = float(t['leverage']) if self._positions.get(position_id): # already exists, just update the profit_loss_percent value # retrieve the position position = self._positions.get(position_id) position.profit_loss_rate = profit_loss_percent * 0.01 continue # # get position details # var = { 'pretty': 'false', 'token': self.__apitoken, 'position_id': position_id } # https://1broker.com/api/v2/position/shared/get.php?token=Aa508b7a7a5ffba14908bded38a88ee8&pretty=true&position_id=4459904 url = self._base_url + 'position/shared/get.php?' + urllib.parse.urlencode( var) self._conn.request("GET", url) response = self._conn.getresponse() data = response.read() if response.status != 200: Terminal.inst().error( "Http error getting %s open position %s !" % (self.name, position_id)) continue data = json.loads(data) if data['error'] or data['warning']: Terminal.inst().error( "API Error getting %s open position %s !" % (self.name, position_id)) continue p = data['response'] entry_price = float(p['entry_price']) stop_loss = float( p['stop_loss']) if p['stop_loss'] is not None else None take_profit = float( p['take_profit']) if p['take_profit'] is not None else None trailing_stop_loss = p['trailing_stop_loss'] comments = p['comments'] # @todo comments +/- 'upvotes' 'downvotes' 'deleted' 'content' 'comment_id' 'username' 'user_id' 'date_created' # => make a confidence score + alert at threshold # quantity is not known from the copier, let it to 0 position.symbol = symbol position.entry(direction, 0.0, entry_price, stop_loss, take_profit, date_created, leverage=leverage, trailing_stop_loss=trailing_stop_loss) # add position self._positions[position_id] = position self.service.notify(Signal.SIGNAL_SOCIAL_ENTER, self.name, position) for t in closed_trades: position_id = t['position_id'] # retrieve the position position = self._positions.get(position_id) if position is None: # Terminal.inst().error("Closed position %s cannot be found !" % (position_id,)) continue if position.status == Position.POSITION_CLOSED: # already closed continue # # get position details # var = { 'pretty': 'false', 'token': self.__apitoken, 'position_id': position_id } # https://1broker.com/api/v2/position/shared/get.php?token=Aa508b7a7a5ffba14908bded38a88ee8&pretty=true&position_id=4459904 url = self._base_url + 'position/shared/get.php?' + urllib.parse.urlencode( var) self._conn.request("GET", url) response = self._conn.getresponse() data = response.read() if response.status != 200: Terminal.inst().error( "Http error getting %s closed position %s !" % (self.name, position_id)) continue data = json.loads(data) if data['error'] or data['warning']: Terminal.inst().error( "API Error getting %s closed position %s !" % (self.name, position_id)) continue p = data['response'] profit_loss_percent = float(t['profit_loss_percent']) exit_price = float( p['exit_price']) if p['exit_price'] is not None else None date_closed = datetime.strptime(t['date_closed'], "%Y-%m-%dT%H:%M:%SZ") # .%fZ") position.exit(exit_price, date_closed) position.profit_loss_rate = profit_loss_percent * 0.01 # Terminal.inst().info("Exited position %s found !" % (position_id,), view='status') self.service.notify(Signal.SIGNAL_SOCIAL_EXIT, self.name, position) self.unlock()
def check_ohlcs(broker_id, market_id, timeframe, from_date, to_date): last_ohlcs = {} ohlc_streamer = Database.inst().create_ohlc_streamer(broker_id, market_id, timeframe, from_date=from_date, to_date=to_date, buffer_size=100) timestamp = from_date.timestamp() to_timestamp = to_date.timestamp() progression = 0.0 prev_update = timestamp count = 0 total_count = 0 progression_incr = (to_timestamp - timestamp) * 0.01 tts = 0.0 prev_tts = 0.0 while not ohlc_streamer.finished(): ohlcs = ohlc_streamer.next(timestamp + timeframe * 100) # per 100 count = len(ohlcs) total_count += len(ohlcs) for ohlc in ohlcs: tts = ohlc.timestamp if not prev_tts: prev_tts = tts gap_duration = tts - prev_tts if gap_duration > timeframe: date = format_datetime(timestamp) Terminal.inst().warning("Ohlc gap of %s on %s !" % (format_delta(gap_duration), date)) if ohlc.bid_open <= 0.0: Terminal.inst().warning("Bid open price is lesser than 0 %s on %s !" % (ohlc.bid_open, date)) if ohlc.bid_high <= 0.0: Terminal.inst().warning("Bid high price is lesser than 0 %s on %s !" % (ohlc.bid_high, date)) if ohlc.bid_low <= 0.0: Terminal.inst().warning("Bid close price is lesser than 0 %s on %s !" % (ohlc.bid_low, date)) if ohlc.bid_close <= 0.0: Terminal.inst().warning("Bid close price is lesser than 0 %s on %s !" % (ohlc.bid_close, date)) if ohlc.ofr_open <= 0.0: Terminal.inst().warning("Ofr open price is lesser than 0 %s on %s !" % (ohlc.ofr_open, date)) if ohlc.ofr_high <= 0.0: Terminal.inst().warning("Ofr high price is lesser than 0 %s on %s !" % (ohlc.ofr_high, date)) if ohlc.ofr_low <= 0.0: Terminal.inst().warning("Ofr low price is lesser than 0 %s on %s !" % (ohlc.ofr_low, date)) if ohlc.ofr_close <= 0.0: Terminal.inst().warning("Ofr close price is lesser than 0 %s on %s !" % (ohlc.ofr_close, date)) if ohlc.volume < 0.0: Terminal.inst().warning("Volume quantity is lesser than 0 %s on %s !" % (ohlc.volume, date)) prev_tts = tts timestamp = tts if timestamp > to_timestamp: break if timestamp - prev_update >= progression_incr: progression += 1 Terminal.inst().info("%i%% on %s, %s for last 100 candles, current total of %s..." % (progression, format_datetime(timestamp), count, total_count)) prev_update = timestamp count = 0 if timestamp > to_timestamp: break if total_count == 0: timestamp += timeframe * 100 if progression < 100: Terminal.inst().info("100%% on %s, %s for last 100 candles, current total of %s..." % (format_datetime(timestamp), count, total_count)) Terminal.inst().info("Last candle datetime is %s" % (format_datetime(tts),))
def do_optimizer(options): Terminal.inst().info("Starting SIIS optimizer...") Terminal.inst().flush() # database manager Database.create(options) Database.inst().setup(options) broker_id = options['broker'] market_id = options['market'] timeframe = None from_date = options.get('from') to_date = options.get('to') if not to_date: today = datetime.now().astimezone(UTC()) if timeframe == Instrument.TF_MONTH: to_date = today + timedelta(months=1) else: to_date = today + timedelta(seconds=timeframe) to_date = to_date.replace(microsecond=0) if not options.get('timeframe'): timeframe = None else: if options['timeframe'] in TIMEFRAME_FROM_STR_MAP: timeframe = TIMEFRAME_FROM_STR_MAP[options['timeframe']] else: try: timeframe = int(options['timeframe']) except: pass try: # checking data integrity, gap... if timeframe is None: for market in options['market'].split(','): if market.startswith('!') or market.startswith('*'): continue for tf in GENERATED_TF: Terminal.inst().info("Verifying %s OHLC %s..." % (market, timeframe_to_str(tf))) check_ohlcs(options['broker'], market, tf, from_date, to_date) elif timeframe == Instrument.TF_TICK: for market in options['market'].split(','): if market.startswith('!') or market.startswith('*'): continue Terminal.inst().info("Verifying %s ticks/trades..." % (market,)) check_ticks(options['broker'], market, from_date, to_date) elif timeframe > 0: # particular ohlc for market in options['market'].split(','): if market.startswith('!') or market.startswith('*'): continue Terminal.inst().info("Verifying %s OHLC %s..." % (market, timeframe_to_str(timeframe))) check_ohlcs(options['broker'], market, timeframe, from_date, to_date) except KeyboardInterrupt: pass finally: pass Terminal.inst().info("Flushing database...") Terminal.inst().flush() Database.terminate() Terminal.inst().info("Optimization done!") Terminal.inst().flush() Terminal.terminate() sys.exit(0)