]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blobdiff - portfolio.py
Various fixes/improvements
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / portfolio.py
index eb3390ed70d2c156f90002afa994e91a14ddf651..b77850bfbe1021b30e704dbd34b7c41d19ec5316 100644 (file)
@@ -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
+from ccxt import ExchangeError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, OrderNotCached
 from retry import retry
 import requests
 
@@ -226,8 +226,8 @@ class Amount:
 
 class Balance:
     base_keys = ["total", "exchange_total", "exchange_used",
-            "exchange_free", "margin_total", "margin_borrowed",
-            "margin_free"]
+            "exchange_free", "margin_total", "margin_in_position",
+            "margin_available", "margin_borrowed", "margin_pending_gain"]
 
     def __init__(self, currency, hash_):
         self.currency = currency
@@ -240,8 +240,8 @@ class Balance:
             base_currency = hash_["margin_borrowed_base_currency"]
             for key in [
                     "margin_liquidation_price",
-                    "margin_pending_gain",
                     "margin_lending_fees",
+                    "margin_pending_base_gain",
                     "margin_borrowed_base_price"
                     ]:
                 setattr(self, key, Amount(base_currency, hash_.get(key, 0)))
@@ -261,12 +261,12 @@ class Balance:
             exchange = ""
 
         if self.margin_total > 0:
-            if self.margin_free != 0 and self.margin_borrowed != 0:
-                margin = " Margin: [✔{} + borrowed {} = {}]".format(str(self.margin_free), str(self.margin_borrowed), str(self.margin_total))
-            elif self.margin_free != 0:
-                margin = " Margin: [✔{}]".format(str(self.margin_free))
+            if self.margin_available != 0 and self.margin_in_position != 0:
+                margin = " Margin: [✔{} + ❌{} = {}]".format(str(self.margin_available), str(self.margin_in_position), str(self.margin_total))
+            elif self.margin_available != 0:
+                margin = " Margin: [✔{}]".format(str(self.margin_available))
             else:
-                margin = " Margin: [borrowed {}]".format(str(self.margin_borrowed))
+                margin = " Margin: [❌{}]".format(str(self.margin_in_position))
         elif self.margin_total < 0:
             margin = " Margin: [{} @@ {}/{}]".format(str(self.margin_total),
                     str(self.margin_borrowed_base_price),
@@ -290,6 +290,7 @@ class Trade:
         self.value_to = value_to
         self.orders = []
         self.market = market
+        assert self.value_from.value * self.value_to.value >= 0
         assert self.value_from.currency == self.value_to.currency
         if self.value_from != 0:
             assert self.value_from.linked_to is not None and self.value_from.linked_to.currency == self.currency
@@ -297,6 +298,10 @@ class Trade:
             self.value_from.linked_to = Amount(self.currency, 0)
         self.base_currency = self.value_from.currency
 
+    @property
+    def delta(self):
+        return self.value_to - self.value_from
+
     @property
     def action(self):
         if self.value_from == self.value_to:
@@ -322,6 +327,10 @@ class Trade:
         else:
             return "long"
 
+    @property
+    def is_fullfiled(self):
+        return abs(self.filled_amount(in_base_currency=True)) >= abs(self.delta)
+
     def filled_amount(self, in_base_currency=False):
         filled_amount = 0
         for order in self.orders:
@@ -329,34 +338,36 @@ class Trade:
         return filled_amount
 
     def update_order(self, order, tick):
-        new_order = None
-        if tick in [0, 1, 3, 4, 6]:
+        actions = {
+                0: ["waiting", None],
+                1: ["waiting", None],
+                2: ["adjusting", lambda x, y: (x[y] + x["average"]) / 2],
+                3: ["waiting", None],
+                4: ["waiting", None],
+                5: ["adjusting", lambda x, y: (x[y]*2 + x["average"]) / 3],
+                6: ["waiting", None],
+                7: ["market_fallback", "default"],
+                }
+
+        if tick in actions:
+            update, compute_value = actions[tick]
+        elif tick % 3 == 1:
+            update = "market_adjust"
+            compute_value = "default"
+        else:
             update = "waiting"
             compute_value = None
-        elif tick == 2:
-            update = "adjusting"
-            compute_value = 'lambda x, y: (x[y] + x["average"]) / 2'
-            new_order = self.prepare_order(compute_value=lambda x, y: (x[y] + x["average"]) / 2)
-        elif tick ==5:
-            update = "adjusting"
-            compute_value = 'lambda x, y: (x[y]*2 + x["average"]) / 3'
-            new_order = self.prepare_order(compute_value=lambda x, y: (x[y]*2 + x["average"]) / 3)
-        elif tick >= 7:
-            if (tick - 7) % 3 == 0:
-                new_order = self.prepare_order(compute_value="default")
-                update = "market_adjust"
-                compute_value = "default"
-            else:
-                update = "waiting"
-                compute_value = None
-            if tick == 7:
-                update = "market_fallback"
+
+        if compute_value is not None:
+            order.cancel()
+            new_order = self.prepare_order(compute_value=compute_value)
+        else:
+            new_order = None
 
         self.market.report.log_order(order, tick, update=update,
                 compute_value=compute_value, new_order=new_order)
 
         if new_order is not None:
-            order.cancel()
             new_order.run()
             self.market.report.log_order(order, tick, new_order=new_order)
 
@@ -369,10 +380,9 @@ class Trade:
             ticker = ticker["original"]
         rate = Computation.compute_value(ticker, self.order_action(inverted), compute_value=compute_value)
 
-        #TODO: store when the order is considered filled
         # FIXME: Dust amount should be removed from there if they werent
         # honored in other sales
-        delta_in_base = abs(self.value_from - self.value_to)
+        delta_in_base = abs(self.delta)
         # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case)
 
         if not inverted:
@@ -477,7 +487,6 @@ class Order:
         self.trade = trade
         self.close_if_possible = close_if_possible
         self.id = None
-        self.fetch_cache_timestamp = None
         self.tries = 0
 
     def as_json(self):
@@ -578,21 +587,19 @@ class Order:
             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)
 
-    def fetch(self, force=False):
+    def fetch(self):
         if self.market.debug:
             self.market.report.log_debug_action("Fetching {}".format(self))
             return
-        if (not force and self.fetch_cache_timestamp is not None
-                and time.time() - self.fetch_cache_timestamp < 10):
-            return
-        self.fetch_cache_timestamp = time.time()
-
-        result = self.market.ccxt.fetch_order(self.id, symbol=self.amount.currency)
-        self.results.append(result)
+        try:
+            result = self.market.ccxt.fetch_order(self.id, symbol=self.amount.currency)
+            self.results.append(result)
+            self.status = result["status"]
+            # Time at which the order started
+            self.timestamp = result["datetime"]
+        except OrderNotCached:
+            self.status = "closed_unknown"
 
-        self.status = result["status"]
-        # Time at which the order started
-        self.timestamp = result["datetime"]
         self.fetch_mouvements()
 
         # FIXME: consider open order with dust remaining as closed
@@ -601,8 +608,6 @@ class Order:
         return self.remaining_amount() < Amount(self.amount.currency, D("0.001"))
 
     def remaining_amount(self):
-        if self.status == "open":
-            self.fetch()
         return self.amount - self.filled_amount()
 
     def filled_amount(self, in_base_currency=False):
@@ -633,7 +638,7 @@ class Order:
             self.status = "canceled"
             return
         self.market.ccxt.cancel_order(self.id)
-        self.fetch(force=True)
+        self.fetch()
 
 class Mouvement:
     def __init__(self, currency, base_currency, hash_):