]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blobdiff - market.py
Add processors
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / market.py
index deaab881f1e2f44864374d4a467f15a367a30bfe..3381d1e2643e03cb35019cbd14eb29f0f9108cbf 100644 (file)
--- a/market.py
+++ b/market.py
@@ -1,7 +1,145 @@
+from ccxt import ExchangeError, NotSupported
 import ccxt_wrapper as ccxt
+import time
+from store import *
+from cachetools.func import ttl_cache
 
-market = ccxt.poloniex({
-    "apiKey": "XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX",
-    "secret": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
-    })
+class Market:
+    debug = False
+    ccxt = None
+    report = None
+    trades = None
+    balances = None
+
+    def __init__(self, ccxt_instance, debug=False):
+        self.debug = debug
+        self.ccxt = ccxt_instance
+        self.ccxt._market = self
+        self.report = ReportStore(self)
+        self.trades = TradeStore(self)
+        self.balances = BalanceStore(self)
+
+    @classmethod
+    def from_config(cls, config, debug=False):
+        config["apiKey"] = config.pop("key")
+
+        ccxt_instance = ccxt.poloniexE(config)
+
+        # For requests logging
+        ccxt_instance.session.origin_request = ccxt_instance.session.request
+        ccxt_instance.session._parent = ccxt_instance
+
+        def request_wrap(self, *args, **kwargs):
+            r = self.origin_request(*args, **kwargs)
+            self._parent._market.report.log_http_request(args[0],
+                    args[1], kwargs["data"], kwargs["headers"], r)
+            return r
+        ccxt_instance.session.request = request_wrap.__get__(ccxt_instance.session,
+                ccxt_instance.session.__class__)
+
+        return cls(ccxt_instance, debug=debug)
+
+    def move_balances(self):
+        needed_in_margin = {} 
+        moving_to_margin = {}
+
+        for currency, balance in self.balances.all.items():
+            needed_in_margin[currency] = balance.margin_in_position - balance.margin_pending_gain
+        for trade in self.trades.pending:
+            needed_in_margin.setdefault(trade.base_currency, 0)
+            if trade.trade_type == "short":
+                needed_in_margin[trade.base_currency] -= trade.delta
+        for currency, needed in needed_in_margin.items():
+            current_balance = self.balances.all[currency].margin_available
+            moving_to_margin[currency] = (needed - current_balance)
+            delta = moving_to_margin[currency].value
+            if self.debug and delta != 0:
+                self.report.log_debug_action("Moving {} from exchange to margin".format(moving_to_margin[currency]))
+                continue
+            if delta > 0:
+                self.ccxt.transfer_balance(currency, delta, "exchange", "margin")
+            elif delta < 0:
+                self.ccxt.transfer_balance(currency, -delta, "margin", "exchange")
+        self.report.log_move_balances(needed_in_margin, moving_to_margin)
+
+        self.balances.fetch_balances()
+
+    @ttl_cache(ttl=3600)
+    def fetch_fees(self):
+        return self.ccxt.fetch_fees()
+
+    @ttl_cache(maxsize=20, ttl=5)
+    def get_tickers(self, refresh=False):
+        try:
+            return self.ccxt.fetch_tickers()
+        except NotSupported:
+            return None
+
+    @ttl_cache(maxsize=20, ttl=5)
+    def get_ticker(self, c1, c2, refresh=False):
+        def invert(ticker):
+            return {
+                    "inverted": True,
+                    "average": (1/ticker["bid"] + 1/ticker["ask"]) / 2,
+                    "original": ticker,
+                    }
+        def augment_ticker(ticker):
+            ticker.update({
+                "inverted": False,
+                "average": (ticker["bid"] + ticker["ask"] ) / 2,
+                })
+            return ticker
+
+        tickers = self.get_tickers()
+        if tickers is None:
+            try:
+                ticker = augment_ticker(self.ccxt.fetch_ticker("{}/{}".format(c1, c2)))
+            except ExchangeError:
+                try:
+                    ticker = invert(augment_ticker(self.ccxt.fetch_ticker("{}/{}".format(c2, c1))))
+                except ExchangeError:
+                    ticker = None
+        else:
+            if "{}/{}".format(c1, c2) in tickers:
+                ticker = augment_ticker(tickers["{}/{}".format(c1, c2)])
+            elif "{}/{}".format(c2, c1) in tickers:
+                ticker = invert(augment_ticker(tickers["{}/{}".format(c2, c1)]))
+            else:
+                ticker = None
+        return ticker
+
+    def follow_orders(self, sleep=None):
+        if sleep is None:
+            sleep = 7 if self.debug else 30
+            if self.debug:
+                self.report.log_debug_action("Set follow_orders tick to {}s".format(sleep))
+        tick = 0
+        self.report.log_stage("follow_orders_begin")
+        while len(self.trades.all_orders(state="open")) > 0:
+            time.sleep(sleep)
+            tick += 1
+            open_orders = self.trades.all_orders(state="open")
+            self.report.log_stage("follow_orders_tick_{}".format(tick))
+            self.report.log_orders(open_orders, tick=tick)
+            for order in open_orders:
+                if order.get_status() != "open":
+                    self.report.log_order(order, tick, finished=True)
+                else:
+                    order.trade.update_order(order, tick)
+        self.report.log_stage("follow_orders_end")
+
+    def prepare_trades(self, base_currency="BTC", liquidity="medium",
+            compute_value="average", repartition=None, only=None):
+
+        self.report.log_stage("prepare_trades",
+                base_currency=base_currency, liquidity=liquidity,
+                compute_value=compute_value, only=only,
+                repartition=repartition)
+
+        values_in_base = self.balances.in_currency(base_currency,
+                compute_value=compute_value)
+        total_base_value = sum(values_in_base.values())
+        new_repartition = self.balances.dispatch_assets(total_base_value,
+                liquidity=liquidity, repartition=repartition)
+        self.trades.compute_trades(values_in_base, new_repartition, only=only)