]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blobdiff - portfolio.py
Add Makefile and test coverage
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / portfolio.py
index efd9b84e498e20f39f0eb5a7e35d464d2b81614a..b629966b3e057cd53e50220be244e686d5418ad9 100644 (file)
@@ -149,7 +149,7 @@ class Amount:
 
     def __floordiv__(self, value):
         if not isinstance(value, (int, float, D)):
-            raise TypeError("Amount may only be multiplied by integers")
+            raise TypeError("Amount may only be divided by numbers")
         return Amount(self.currency, self.value / value)
 
     def __truediv__(self, value):
@@ -290,11 +290,10 @@ class Trade:
         else:
             return "long"
 
-    @property
-    def filled_amount(self):
+    def filled_amount(self, in_base_currency=False):
         filled_amount = 0
         for order in self.orders:
-            filled_amount += order.filled_amount
+            filled_amount += order.filled_amount(in_base_currency=in_base_currency)
         return filled_amount
 
     def update_order(self, order, tick):
@@ -329,54 +328,69 @@ class Trade:
         if inverted:
             ticker = ticker["original"]
         rate = Computation.compute_value(ticker, self.order_action(inverted), compute_value=compute_value)
-        # 0.1
 
         delta_in_base = abs(self.value_from - self.value_to)
         # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case)
 
         if not inverted:
-            currency = self.base_currency
+            base_currency = self.base_currency
             # BTC
             if self.action == "dispose":
-                # I have 10 BTC worth of FOO, and I want to sell 9 BTC worth of it
-                # At rate 1 Foo = 0.1 BTC
-                value_from = self.value_from.linked_to
-                # value_from = 100 FOO
-                value_to = self.value_to.in_currency(self.currency, self.market, rate=1/self.value_from.rate)
-                # value_to   = 10 FOO (1 BTC * 1/0.1)
-                delta = abs(value_to - value_from)
-                # delta      = 90 FOO
-                # Action: "sell" "90 FOO" at rate "0.1" "BTC" on "market"
-
-                # Note: no rounding error possible: if we have value_to == 0, then delta == value_from
+                filled = self.filled_amount(in_base_currency=False)
+                delta = delta_in_base.in_currency(self.currency, self.market, rate=1/self.value_from.rate)
+                # I have 10 BTC worth of FOO, and I want to sell 9 BTC
+                # worth of it, computed first with rate 10 FOO = 1 BTC.
+                # -> I "sell" "90" FOO at proposed rate "rate".
+
+                delta = delta - filled
+                # I already sold 60 FOO, 30 left
             else:
-                delta = delta_in_base.in_currency(self.currency, self.market, rate=1/rate)
-                # I want to buy 9 / 0.1 FOO
-                # Action: "buy" "90 FOO" at rate "0.1" "BTC" on "market"
+                filled = self.filled_amount(in_base_currency=True)
+                delta = (delta_in_base - filled).in_currency(self.currency, self.market, rate=1/rate)
+                # I want to buy 9 BTC worth of FOO, computed with rate
+                # 10 FOO = 1 BTC
+                # -> I "buy" "9 / rate" FOO at proposed rate "rate"
+
+                # I already bought 3 / rate FOO, 6 / rate left
         else:
-            currency = self.currency
+            base_currency = self.currency
             # FOO
-            delta = delta_in_base
-            # sell: 
-            #   I have 10 BTC worth of FOO, and I want to sell 9 BTC worth of it
-            #   At rate 1 Foo = 0.1 BTC
-            #   Action: "buy" "9 BTC" at rate "1/0.1" "FOO" on market
-            # buy:
-            #   I want to buy 9 / 0.1 FOO
-            #   Action: "sell" "9 BTC" at rate "1/0.1" "FOO" on "market"
-            if self.value_to == 0:
-                rate = self.value_from.linked_to.value / self.value_from.value
-                # Recompute the rate to avoid any rounding error
+            if self.action == "dispose":
+                filled = self.filled_amount(in_base_currency=True)
+                # Base is FOO
+
+                delta = (delta_in_base.in_currency(self.currency, self.market, rate=1/self.value_from.rate)
+                        - filled).in_currency(self.base_currency, self.market, rate=1/rate)
+                # I have 10 BTC worth of FOO, and I want to sell 9 BTC worth of it
+                # computed at rate 1 Foo = 0.01 BTC
+                # Computation says I should sell it at 125 FOO / BTC
+                # -> delta_in_base = 9 BTC
+                # -> delta = (9 * 1/0.01 FOO) * 1/125 = 7.2 BTC
+                # Action: "buy" "7.2 BTC" at rate "125" "FOO" on market
+
+                # I already bought 300/125 BTC, only 600/125 left
+            else:
+                filled = self.filled_amount(in_base_currency=False)
+                # Base is FOO
+
+                delta = delta_in_base
+                # I have 1 BTC worth of FOO, and I want to buy 9 BTC worth of it
+                # At rate 100 Foo / BTC
+                # Computation says I should buy it at 125 FOO / BTC
+                # -> delta_in_base = 9 BTC
+                # Action: "sell" "9 BTC" at rate "125" "FOO" on market
+
+                delta = delta - filled
+                # I already sold 4 BTC, only 5 left
 
         close_if_possible = (self.value_to == 0)
 
-        if delta <= self.filled_amount:
-            print("Less to do than already filled: {} <= {}".format(delta,
-                self.filled_amount))
+        if delta <= 0:
+            print("Less to do than already filled: {}".format(delta))
             return
 
         self.orders.append(Order(self.order_action(inverted),
-            delta - self.filled_amount, rate, currency, self.trade_type,
+            delta, rate, base_currency, self.trade_type,
             self.market, self, close_if_possible=close_if_possible))
 
     def __repr__(self):
@@ -497,15 +511,17 @@ class Order:
     def remaining_amount(self):
         if self.status == "open":
             self.fetch()
-        return self.amount - self.filled_amount
+        return self.amount - self.filled_amount()
 
-    @property
-    def filled_amount(self):
+    def filled_amount(self, in_base_currency=False):
         if self.status == "open":
             self.fetch()
-        filled_amount = Amount(self.amount.currency, 0)
+        filled_amount = 0
         for mouvement in self.mouvements:
-            filled_amount += mouvement.total
+            if in_base_currency:
+                filled_amount += mouvement.total_in_base
+            else:
+                filled_amount += mouvement.total
         return filled_amount
 
     def fetch_mouvements(self):