X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=portfolio.py;h=ed50b570ea2c31d310c6a3d271f37f7f413c43c0;hb=bd7ba362442f27fe3f53729a0040f2473b85a068;hp=f27e84f0953200bf64606734f827ec30a2a25de6;hpb=17598517c544a3dda8b9f773dfeb669c886ea92b;p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FCryptoportfolio%2FTrader.git diff --git a/portfolio.py b/portfolio.py index f27e84f..ed50b57 100644 --- a/portfolio.py +++ b/portfolio.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from decimal import Decimal as D, ROUND_DOWN from json import JSONDecodeError from simplejson.errors import JSONDecodeError as SimpleJSONDecodeError -from ccxt import ExchangeError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, OrderNotCached +from ccxt import ExchangeError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, OrderNotCached, OrderNotFound from retry import retry import requests @@ -291,6 +291,7 @@ class Trade: self.orders = [] self.market = market self.closed = False + self.inverted = None assert self.value_from.value * self.value_to.value >= 0 assert self.value_from.currency == self.value_to.currency if self.value_from != 0: @@ -315,8 +316,8 @@ class Trade: else: return "dispose" - def order_action(self, inverted): - if (self.value_from < self.value_to) != inverted: + def order_action(self): + if (self.value_from < self.value_to) != self.inverted: return "buy" else: return "sell" @@ -333,11 +334,13 @@ class Trade: return not (self.is_fullfiled or self.closed) def close(self): + for order in self.orders: + order.cancel() self.closed = True @property def is_fullfiled(self): - return abs(self.filled_amount(in_base_currency=True)) >= abs(self.delta) + return abs(self.filled_amount(in_base_currency=(not self.inverted))) >= abs(self.delta) def filled_amount(self, in_base_currency=False): filled_amount = 0 @@ -383,17 +386,17 @@ class Trade: if self.action is None: return None ticker = self.market.get_ticker(self.currency, self.base_currency) - inverted = ticker["inverted"] - if inverted: + self.inverted = ticker["inverted"] + if self.inverted: ticker = ticker["original"] - rate = Computation.compute_value(ticker, self.order_action(inverted), compute_value=compute_value) + rate = Computation.compute_value(ticker, self.order_action(), compute_value=compute_value) # FIXME: Dust amount should be removed from there if they werent # honored in other sales delta_in_base = abs(self.delta) # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case) - if not inverted: + if not self.inverted: base_currency = self.base_currency # BTC if self.action == "dispose": @@ -451,7 +454,7 @@ class Trade: self.market.report.log_error("prepare_order", message="Less to do than already filled: {}".format(delta)) return None - order = Order(self.order_action(inverted), + order = Order(self.order_action(), delta, rate, base_currency, self.trade_type, self.market, self, close_if_possible=close_if_possible) self.orders.append(order) @@ -467,11 +470,19 @@ class Trade: } def __repr__(self): - return "Trade({} -> {} in {}, {})".format( + if self.closed and not self.is_fullfiled: + closed = " ❌" + elif self.is_fullfiled: + closed = " ✔" + else: + closed = "" + + return "Trade({} -> {} in {}, {}{})".format( self.value_from, self.value_to, self.currency, - self.action) + self.action, + closed) def print_with_order(self, ind=""): self.market.report.print_log("{}{}".format(ind, self)) @@ -539,7 +550,7 @@ class Order: @property def finished(self): - return self.status == "closed" or self.status == "canceled" or self.status == "error" + return self.status.startswith("closed") or self.status == "canceled" or self.status == "error" @retry(InsufficientFunds) def run(self): @@ -583,15 +594,13 @@ class Order: # other states are "closed" and "canceled" if not self.finished: self.fetch() - if self.finished: - self.mark_finished_order() return self.status def mark_finished_order(self): - if self.market.debug: + if self.status.startswith("closed") and self.market.debug: self.market.report.log_debug_action("Mark {} as finished".format(self)) return - if self.status == "closed": + if self.status.startswith("closed"): if self.trade_type == "short" and self.action == "buy" and self.close_if_possible: self.market.ccxt.close_margin_position(self.amount.currency, self.base_currency) @@ -600,7 +609,7 @@ class Order: self.market.report.log_debug_action("Fetching {}".format(self)) return try: - result = self.market.ccxt.fetch_order(self.id, symbol=self.amount.currency) + result = self.market.ccxt.fetch_order(self.id) self.results.append(result) self.status = result["status"] # Time at which the order started @@ -610,6 +619,7 @@ class Order: self.fetch_mouvements() + self.mark_finished_order() # FIXME: consider open order with dust remaining as closed def dust_amount_remaining(self): @@ -645,8 +655,12 @@ class Order: self.market.report.log_debug_action("Mark {} as cancelled".format(self)) self.status = "canceled" return - self.market.ccxt.cancel_order(self.id) - self.fetch() + if self.open and self.id is not None: + try: + self.market.ccxt.cancel_order(self.id) + except OrderNotFound as e: # Closed inbetween + self.market.report.log_error("cancel_order", message="Already cancelled order", exception=e) + self.fetch() class Mouvement: def __init__(self, currency, base_currency, hash_):