]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blobdiff - portfolio.py
Add report store to store messages and logs
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / portfolio.py
index c3809f0533db32cc3b62ccbd6c64a5ff5c66e651..f9423b91d0736178fb4759016195e3d29ecd2499 100644 (file)
@@ -3,6 +3,7 @@ from datetime import datetime, timedelta
 from decimal import Decimal as D, ROUND_DOWN
 # Put your poloniex api key in market.py
 from json import JSONDecodeError
+from simplejson.errors import JSONDecodeError as SimpleJSONDecodeError
 from ccxt import ExchangeError, ExchangeNotAvailable
 import requests
 import helper as h
@@ -33,11 +34,14 @@ class Portfolio:
     def get_cryptoportfolio(cls):
         try:
             r = requests.get(cls.URL)
-        except Exception:
+            ReportStore.log_http_request(r.request.method,
+                    r.request.url, r.request.body, r.request.headers, r)
+        except Exception as e:
+            ReportStore.log_error("get_cryptoportfolio", exception=e)
             return
         try:
             cls.data = r.json(parse_int=D, parse_float=D)
-        except JSONDecodeError:
+        except (JSONDecodeError, SimpleJSONDecodeError):
             cls.data = None
 
     @classmethod
@@ -126,6 +130,12 @@ class Amount:
         else:
             raise Exception("This asset is not available in the chosen market")
 
+    def as_json(self):
+        return {
+                "currency": self.currency,
+                "value": round(self).value.normalize(),
+                }
+
     def __round__(self, n=8):
         return Amount(self.currency, self.value.quantize(D(1)/D(10**n), rounding=ROUND_DOWN))
 
@@ -216,12 +226,13 @@ class Amount:
             return "Amount({:.8f} {} -> {})".format(self.value, self.currency, repr(self.linked_to))
 
 class Balance:
+    base_keys = ["total", "exchange_total", "exchange_used",
+            "exchange_free", "margin_total", "margin_borrowed",
+            "margin_free"]
 
     def __init__(self, currency, hash_):
         self.currency = currency
-        for key in ["total",
-                "exchange_total", "exchange_used", "exchange_free",
-                "margin_total", "margin_borrowed", "margin_free"]:
+        for key in self.base_keys:
             setattr(self, key, Amount(currency, hash_.get(key, 0)))
 
         self.margin_position_type = hash_.get("margin_position_type")
@@ -236,6 +247,9 @@ class Balance:
                     ]:
                 setattr(self, key, Amount(base_currency, hash_.get(key, 0)))
 
+    def as_json(self):
+        return dict(map(lambda x: (x, getattr(self, x).as_json()["value"]), self.base_keys))
+
     def __repr__(self):
         if self.exchange_total > 0:
             if self.exchange_free > 0 and self.exchange_used > 0:
@@ -318,23 +332,34 @@ class Trade:
     def update_order(self, order, tick):
         new_order = None
         if tick in [0, 1, 3, 4, 6]:
-            print("{}, tick {}, waiting".format(order, tick))
+            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)
-            print("{}, tick {}, cancelling and adjusting to {}".format(order, tick, new_order))
         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)
-            print("{}, tick {}, cancelling and adjusting to {}".format(order, tick, new_order))
         elif tick >= 7:
-            if tick == 7:
-                print("{}, tick {}, fallbacking to market value".format(order, tick))
             if (tick - 7) % 3 == 0:
                 new_order = self.prepare_order(compute_value="default")
-                print("{}, tick {}, market value, cancelling and adjusting to {}".format(order, tick, new_order))
+                update = "market_adjust"
+                compute_value = "default"
+            else:
+                update = "waiting"
+                compute_value = None
+            if tick == 7:
+                update = "market_fallback"
+
+        ReportStore.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()
+            ReportStore.log_order(order, tick, new_order=new_order)
 
     def prepare_order(self, compute_value="default"):
         if self.action is None:
@@ -405,7 +430,7 @@ class Trade:
         close_if_possible = (self.value_to == 0)
 
         if delta <= 0:
-            print("Less to do than already filled: {}".format(delta))
+            ReportStore.log_error("prepare_order", message="Less to do than already filled: {}".format(delta))
             return None
 
         order = Order(self.order_action(inverted),
@@ -414,6 +439,15 @@ class Trade:
         self.orders.append(order)
         return order
 
+    def as_json(self):
+        return {
+                "action": self.action,
+                "from": self.value_from.as_json()["value"],
+                "to": self.value_to.as_json()["value"],
+                "currency": self.currency,
+                "base_currency": self.base_currency,
+                }
+
     def __repr__(self):
         return "Trade({} -> {} in {}, {})".format(
                 self.value_from,
@@ -421,12 +455,12 @@ class Trade:
                 self.currency,
                 self.action)
 
-    def print_with_order(self):
-        print(self)
+    def print_with_order(self, ind=""):
+        ReportStore.print_log("{}{}".format(ind, self))
         for order in self.orders:
-            print("\t", order, sep="")
+            ReportStore.print_log("{}\t{}".format(ind, order))
             for mouvement in order.mouvements:
-                print("\t\t", mouvement, sep="")
+                ReportStore.print_log("{}\t\t{}".format(ind, mouvement))
 
 class Order:
     def __init__(self, action, amount, rate, base_currency, trade_type, market,
@@ -445,6 +479,20 @@ class Order:
         self.id = None
         self.fetch_cache_timestamp = None
 
+    def as_json(self):
+        return {
+                "action": self.action,
+                "trade_type": self.trade_type,
+                "amount": self.amount.as_json()["value"],
+                "currency": self.amount.as_json()["currency"],
+                "base_currency": self.base_currency,
+                "rate": self.rate,
+                "status": self.status,
+                "close_if_possible": self.close_if_possible,
+                "id": self.id,
+                "mouvements": list(map(lambda x: x.as_json(), self.mouvements))
+                }
+
     def __repr__(self):
         return "Order({} {} {} at {} {} [{}]{})".format(
                 self.action,
@@ -480,7 +528,7 @@ class Order:
         amount = round(self.amount, self.market.order_precision(symbol)).value
 
         if TradeStore.debug:
-            print("market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format(
+            ReportStore.log_debug_action("market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format(
                 symbol, self.action, amount, self.rate, self.account))
             self.results.append({"debug": True, "id": -1})
         else:
@@ -493,16 +541,15 @@ class Order:
                 return
             except Exception as e:
                 self.status = "error"
-                print("error when running market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format(
-                    symbol, self.action, amount, self.rate, self.account))
-                self.error_message = str("{}: {}".format(e.__class__.__name__, e))
-                print(self.error_message)
+                action = "market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format(symbol, self.action, amount, self.rate, self.account)
+                ReportStore.log_error(action, exception=e)
                 return
         self.id = self.results[0]["id"]
         self.status = "open"
 
     def get_status(self):
         if TradeStore.debug:
+            ReportStore.log_debug_action("Getting {} status".format(self))
             return self.status
         # other states are "closed" and "canceled"
         if not self.finished:
@@ -513,13 +560,17 @@ class Order:
 
     def mark_finished_order(self):
         if TradeStore.debug:
+            ReportStore.log_debug_action("Mark {} as finished".format(self))
             return
         if self.status == "closed":
             if self.trade_type == "short" and self.action == "buy" and self.close_if_possible:
                 self.market.close_margin_position(self.amount.currency, self.base_currency)
 
     def fetch(self, force=False):
-        if TradeStore.debug or (not force and self.fetch_cache_timestamp is not None
+        if TradeStore.debug:
+            ReportStore.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()
@@ -566,6 +617,7 @@ class Order:
 
     def cancel(self):
         if TradeStore.debug:
+            ReportStore.log_debug_action("Mark {} as cancelled".format(self))
             self.status = "canceled"
             return
         self.market.cancel_order(self.id)
@@ -587,6 +639,17 @@ class Mouvement:
         # rate * total = total_in_base
         self.total_in_base = Amount(base_currency, hash_.get("total", 0))
 
+    def as_json(self):
+        return {
+                "fee_rate": self.fee_rate,
+                "date": self.date,
+                "action": self.action,
+                "total": self.total.value,
+                "currency": self.currency,
+                "total_in_base": self.total_in_base.value,
+                "base_currency": self.base_currency
+                }
+
     def __repr__(self):
         if self.fee_rate > 0:
             fee_rate = " fee: {}%".format(self.fee_rate * 100)