]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blobdiff - portfolio.py
Add print_balances helper
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / portfolio.py
index e98689ee4c2599fc63f77b6236057e9df0b2519e..482d0da5d050fb466589f584ea617c61cd6e94df 100644 (file)
@@ -1,8 +1,9 @@
 import time
-from datetime import datetime
+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 ccxt import ExchangeError, ExchangeNotAvailable
 import requests
 import helper as h
 from store import *
@@ -13,12 +14,19 @@ class Portfolio:
     URL = "https://cryptoportfolio.io/wp-content/uploads/portfolio/json/cryptoportfolio.json"
     liquidities = {}
     data = None
+    last_date = None
 
     @classmethod
-    def repartition(cls, liquidity="medium"):
-        cls.parse_cryptoportfolio()
+    def wait_for_recent(cls, delta=4):
+        cls.repartition(refetch=True)
+        while cls.last_date is None or datetime.now() - cls.last_date > timedelta(delta):
+            time.sleep(30)
+            cls.repartition(refetch=True)
+
+    @classmethod
+    def repartition(cls, liquidity="medium", refetch=False):
+        cls.parse_cryptoportfolio(refetch=refetch)
         liquidities = cls.liquidities[liquidity]
-        cls.last_date = sorted(liquidities.keys())[-1]
         return liquidities[cls.last_date]
 
     @classmethod
@@ -33,8 +41,8 @@ class Portfolio:
             cls.data = None
 
     @classmethod
-    def parse_cryptoportfolio(cls):
-        if cls.data is None:
+    def parse_cryptoportfolio(cls, refetch=False):
+        if refetch or cls.data is None:
             cls.get_cryptoportfolio()
 
         def filter_weights(weight_hash):
@@ -56,7 +64,8 @@ class Portfolio:
             weights_hash = portfolio_hash["weights"]
             weights = {}
             for i in range(len(weights_hash["_row"])):
-                weights[weights_hash["_row"][i]] = dict(filter(
+                date = datetime.strptime(weights_hash["_row"][i], "%Y-%m-%d")
+                weights[date] = dict(filter(
                         filter_weights,
                         map(clean_weights(i), weights_hash.items())))
             return weights
@@ -68,6 +77,7 @@ class Portfolio:
                 "medium": medium_liquidity,
                 "high":   high_liquidity,
                 }
+        cls.last_date = max(max(medium_liquidity.keys()), max(high_liquidity.keys()))
 
 class Computation:
     computations = {
@@ -123,6 +133,8 @@ class Amount:
         return Amount(self.currency, abs(self.value))
 
     def __add__(self, other):
+        if other == 0:
+            return self
         if other.currency != self.currency and other.value * self.value != 0:
             raise Exception("Summing amounts must be done with same currencies")
         return Amount(self.currency, self.value + other.value)
@@ -140,6 +152,12 @@ class Amount:
             raise Exception("Summing amounts must be done with same currencies")
         return Amount(self.currency, self.value - other.value)
 
+    def __rsub__(self, other):
+        if other == 0:
+            return -self
+        else:
+            return -self.__sub__(other)
+
     def __mul__(self, value):
         if not isinstance(value, (int, float, D)):
             raise TypeError("Amount may only be multiplied by numbers")
@@ -327,6 +345,8 @@ class Trade:
             ticker = ticker["original"]
         rate = Computation.compute_value(ticker, self.order_action(inverted), compute_value=compute_value)
 
+        # 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)
         # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case)
 
@@ -463,6 +483,11 @@ class Order:
         else:
             try:
                 self.results.append(self.market.create_order(symbol, 'limit', self.action, amount, price=self.rate, account=self.account))
+            except ExchangeNotAvailable:
+                # Impossible to honor the order (dust amount)
+                self.status = "closed"
+                self.mark_finished_order()
+                return
             except Exception as e:
                 self.status = "error"
                 print("error when running market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format(
@@ -526,7 +551,10 @@ class Order:
         return filled_amount
 
     def fetch_mouvements(self):
-        mouvements = self.market.privatePostReturnOrderTrades({"orderNumber": self.id})
+        try:
+            mouvements = self.market.privatePostReturnOrderTrades({"orderNumber": self.id})
+        except ExchangeError:
+            mouvements = []
         self.mouvements = []
 
         for mouvement_hash in mouvements:
@@ -544,14 +572,17 @@ class Mouvement:
     def __init__(self, currency, base_currency, hash_):
         self.currency = currency
         self.base_currency = base_currency
-        self.id = hash_["id"]
-        self.action = hash_["type"]
-        self.fee_rate = D(hash_["fee"])
-        self.date = datetime.strptime(hash_["date"], '%Y-%m-%d %H:%M:%S')
-        self.rate = D(hash_["rate"])
-        self.total = Amount(currency, hash_["amount"])
+        self.id = hash_.get("tradeID")
+        self.action = hash_.get("type")
+        self.fee_rate = D(hash_.get("fee", -1))
+        try:
+            self.date = datetime.strptime(hash_.get("date", ""), '%Y-%m-%d %H:%M:%S')
+        except ValueError:
+            self.date = None
+        self.rate = D(hash_.get("rate", 0))
+        self.total = Amount(currency, hash_.get("amount", 0))
         # rate * total = total_in_base
-        self.total_in_base = Amount(base_currency, hash_["total"])
+        self.total_in_base = Amount(base_currency, hash_.get("total", 0))
 
 if __name__ == '__main__': # pragma: no cover
     from market import market