CryptoRobotFr / Live-Tools-V2

GNU General Public License v3.0
24 stars 12 forks source link

"Currently holding positions or orders, the margin mode cannot be adjusted" #3

Closed zac667 closed 7 months ago

zac667 commented 8 months ago

Salut Tristan, super tuto, c'est clair net et précis, cependant une erreur survient lorsqu'on lance le script alors qu'une position est deja ouverte (dans mon cas un short). Apparement on essaye de modifier le levier ou le margin mode.

Il faudrait add une condition pour que si l'ordre est deja ouvert on ne touche à rien.

Voici l'erreur:

--- Execution started at 2024-01-31 18:37:49 --- Pair REN/USDT not found, removing from params... Setting isolated x3 on 21 pairs... Getting data and indicators on 21 pairs... Balance: 58.73 USDT Getting open trigger orders... Canceling trigger orders... Getting open orders... Canceling limit orders... Getting live positions... Current position on BTC/USDT short - 0.001 ~ 43.5 $ Placing 2 close SL / limit order... Traceback (most recent call last): File "Live-Tools-V2/strategies/envelopes/multi_bitget.py", line 443, in asyncio.run(main()) File "/usr/lib/python3.8/asyncio/runners.py", line 44, in run return loop.run_until_complete(main) File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete return future.result() File "Live-Tools-V2/strategies/envelopes/multi_bitget.py", line 439, in main raise e File "Live-Tools-V2/strategies/envelopes/multi_bitget.py", line 381, in main await asyncio.gather(*tasks_close) # Limit orders when in positions File "/home/cryptorobot/./Live-Tools-V2/utilities/bitget_perp.py", line 283, in place_order raise e File "/home/cryptorobot/./Live-Tools-V2/utilities/bitget_perp.py", line 265, in place_order resp = await self._session.create_order( File "/home/cryptorobot/Live-Tools-V2/.venv/lib/python3.8/site-packages/ccxt/async_support/bitget.py", line 3908, in create_order response = await self.privateMixPostV2MixOrderPlaceOrder(request) File "/home/cryptorobot/Live-Tools-V2/.venv/lib/python3.8/site-packages/ccxt/async_support/base/exchange.py", line 753, in request return await self.fetch2(path, api, method, params, headers, body, config) File "/home/cryptorobot/Live-Tools-V2/.venv/lib/python3.8/site-packages/ccxt/async_support/base/exchange.py", line 750, in fetch2 return await self.fetch(request['url'], request['method'], request['headers'], request['body']) File "/home/cryptorobot/Live-Tools-V2/.venv/lib/python3.8/site-packages/ccxt/async_support/base/exchange.py", line 243, in fetch self.handle_errors(http_status_code, http_status_text, url, method, headers, http_response, json_response, request_headers, request_body) File "/home/cryptorobot/Live-Tools-V2/.venv/lib/python3.8/site-packages/ccxt/async_support/bitget.py", line 7684, in handle_errors raise ExchangeError(feedback) # unknown message ccxt.base.errors.ExchangeError: bitget {"code":"45117","msg":"Currently holding positions or orders, the margin mode cannot be adjusted","requestTime":1706722718388,"data":null}

zac667 commented 8 months ago

Bon j'ai trouver une solution, les ordres deja ouvert ont pas l'air d'etre fermer si ils sont en contact avec la moyenne mobile. et j'ai ajouter une condition pour ne pas toucher au leverage et margin mode des ordres deja ouver.

Y'a juste un petit soucis, j'ai pas l'impression que les stop loss s'enclenchent sur les ordres deja ouverts :(

zac667 commented 8 months ago

import datetime import sys import time sys.path.append("./Live-Tools-V2")

import asyncio from utilities.bitget_perp import PerpBitget from secret import ACCOUNTS import ta

if sys.platform == "win32": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

async def main(): account = ACCOUNTS["bitget1"]

margin_mode = "isolated"  # isolated or crossed
exchange_leverage = 5

tf = "5m"
size_leverage = 5
sl = 1
params = {
    "BTC/USDT": {
        "src": "close",
        "ma_base_window": 7,
        "envelopes": [0.01,0.02,0.03],
        "size": 1,
    },

}

exchange = PerpBitget(
    public_api=account["public_api"],
    secret_api=account["secret_api"],
    password=account["password"],
)
invert_side = {"long": "sell", "short": "buy"}
print(f"--- Execution started at {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ---")
try:
    await exchange.load_markets()

    for pair in params.copy():
        info = exchange.get_pair_info(pair)
        if info is None:
            print(f"Pair {pair} not found, removing from params...")
            del params[pair]

    pairs = list(params.keys())

    """ for pair in pairs:
        open_positions = await exchange.get_open_positions(pair)
        open_orders = await exchange.get_open_orders(pair)
        if not open_positions and not open_orders:
            try:
                await exchange.set_margin_mode_and_leverage(pair, margin_mode, exchange_leverage)
                print(f"Margin mode and leverage set for {pair}")
            except Exception as e:
                print(f"Error setting margin mode and leverage for {pair}: {e}")
        else:
            print(f"{pair} has open positions or orders, skipping margin mode and leverage setting...") """

    print(f"Getting data and indicators on {len(pairs)} pairs...")
    tasks = [exchange.get_last_ohlcv(pair, tf, 50) for pair in pairs]
    dfs = await asyncio.gather(*tasks)
    df_list = dict(zip(pairs, dfs))

    for pair in df_list:
        current_params = params[pair]
        df = df_list[pair]
        if current_params["src"] == "close":
            src = df["close"]
        elif current_params["src"] == "ohlc4":
            src = (df["close"] + df["high"] + df["low"] + df["open"]) / 4

        df["ma_base"] = ta.trend.sma_indicator(
            close=src, window=current_params["ma_base_window"]
        )
        high_envelopes = [
            round(1 / (1 - e) - 1, 3) for e in current_params["envelopes"]
        ]
        for i in range(1, len(current_params["envelopes"]) + 1):
            df[f"ma_high_{i}"] = df["ma_base"] * (1 + high_envelopes[i - 1])
            df[f"ma_low_{i}"] = df["ma_base"] * (
                1 - current_params["envelopes"][i - 1]
            )

        df_list[pair] = df

    usdt_balance = await exchange.get_balance()
    usdt_balance = usdt_balance.total
    print(f"Balance: {round(usdt_balance, 2)} USDT")

    tasks = [exchange.get_open_trigger_orders(pair) for pair in pairs]
    print(f"Getting open trigger orders...")
    trigger_orders = await asyncio.gather(*tasks)
    trigger_order_list = dict(
        zip(pairs, trigger_orders)
    )  # Get all open trigger orders by pair

    tasks = []
    for pair in df_list:
        params[pair]["canceled_orders_buy"] = len(
            [
                order
                for order in trigger_order_list[pair]
                if (order.side == "buy" and order.reduce is False)
            ]
        )
        params[pair]["canceled_orders_sell"] = len(
            [
                order
                for order in trigger_order_list[pair]
                if (order.side == "sell" and order.reduce is False)
            ]
        )
        tasks.append(
            exchange.cancel_trigger_orders(
                pair, [order.id for order in trigger_order_list[pair]]
            )
        )
    print(f"Canceling trigger orders...")
    await asyncio.gather(*tasks)  # Cancel all trigger orders

    tasks = [exchange.get_open_orders(pair) for pair in pairs]
    print(f"Getting open orders...")
    orders = await asyncio.gather(*tasks)
    order_list = dict(zip(pairs, orders))  # Get all open orders by pair

    tasks = []
    for pair in df_list:
        params[pair]["canceled_orders_buy"] = params[pair][
            "canceled_orders_buy"
        ] + len(
            [
                order
                for order in order_list[pair]
                if (order.side == "buy" and order.reduce is False)
            ]
        )
        params[pair]["canceled_orders_sell"] = params[pair][
            "canceled_orders_sell"
        ] + len(
            [
                order
                for order in order_list[pair]
                if (order.side == "sell" and order.reduce is False)
            ]
        )
        tasks.append(
            exchange.cancel_orders(pair, [order.id for order in order_list[pair]])
        )

    print(f"Canceling limit orders...")
    await asyncio.gather(*tasks)  # Cancel all orders

    print(f"Getting live positions...")
    positions = await exchange.get_open_positions(pairs)
    tasks_close = []
    tasks_open = []
    for position in positions:
        print(
            f"Current position on {position.pair} {position.side} - {position.size} ~ {position.usd_size} $"
        )
        row = df_list[position.pair].iloc[-2]
        def should_close_position(position, row, threshold=0.01):
            """
            Détermine si une position doit être fermée basée sur une condition spécifique,
            par exemple, si le prix actuel est proche de la moyenne mobile.

            :param position: Un objet position contenant des détails comme le prix d'entrée.
            :param row: La dernière ligne de données de marché pour la paire de trading de la position.
            :param threshold: Le seuil de proximité avec la moyenne mobile pour décider de fermer la position.
            :return: True si la position doit être fermée, False autrement.
            """

            # Calculez la distance entre le prix d'entrée de la position et la moyenne mobile
            distance_from_ma = abs(position.entry_price - row["ma_base"])

            # Vérifiez si la distance est inférieure au seuil spécifié
            if distance_from_ma <= threshold:
                # La condition pour fermer la position est remplie
                return True

            else:
                # La condition pour fermer la position n'est pas remplie
                return False

        if should_close_position(position, row):  # Vous devez définir cette fonction
            tasks_close.append(                    
                exchange.place_order(
                    pair=position.pair,
                    side=invert_side[position.side],
                    price=row["ma_base"],  # ou votre logique de prix spécifique
                    size=position.size,
                    type="limit",
                    reduce=True,
                    margin_mode=margin_mode,
                )
            )

        if position.side == "long":
            sl_side = "sell"
            sl_price = exchange.price_to_precision(position.pair, position.entry_price * (1 - sl))
        elif position.side == "short":
            sl_side = "buy"
            sl_price = exchange.price_to_precision(position.pair, position.entry_price * (1 + sl))
        tasks_close.append(
            exchange.place_trigger_order(
                pair=position.pair,
                side=sl_side,
                trigger_price=sl_price,
                price=None,
                size=position.size,
                type="market",
                reduce=True,
                margin_mode=margin_mode,
                error=False,
            )
        )
        for i in range(
            len(params[position.pair]["envelopes"])
            - params[position.pair]["canceled_orders_buy"],
            len(params[position.pair]["envelopes"]),
        ):
            tasks_open.append(
                exchange.place_trigger_order(
                    pair=position.pair,
                    side="buy",
                    price=exchange.price_to_precision(
                        position.pair, row[f"ma_low_{i+1}"]
                    ),
                    trigger_price=exchange.price_to_precision(
                        position.pair, row[f"ma_low_{i+1}"] * 1.005
                    ),
                    size=(
                        (params[position.pair]["size"] * usdt_balance)
                        / len(params[position.pair]["envelopes"])
                        * size_leverage
                    )
                    / row[f"ma_low_{i+1}"],
                    type="limit",
                    reduce=False,
                    margin_mode=margin_mode,
                    error=False,
                )
            )
        for i in range(
            len(params[position.pair]["envelopes"])
            - params[position.pair]["canceled_orders_sell"],
            len(params[position.pair]["envelopes"]),
        ):
            tasks_open.append(
                exchange.place_trigger_order(
                    pair=position.pair,
                    side="sell",
                    trigger_price=exchange.price_to_precision(
                        position.pair, row[f"ma_high_{i+1}"] * 0.995
                    ),
                    price=exchange.price_to_precision(
                        position.pair, row[f"ma_high_{i+1}"]
                    ),
                    size=(
                        (params[position.pair]["size"] * usdt_balance)
                        / len(params[position.pair]["envelopes"])
                        * size_leverage
                    )
                    / row[f"ma_high_{i+1}"],
                    type="limit",
                    reduce=False,
                    margin_mode=margin_mode,
                    error=False,
                )
            )

    print(f"Placing {len(tasks_close)} close SL / limit order...")
    await asyncio.gather(*tasks_close)  # Limit orders when in positions

    pairs_not_in_position = [
        pair
        for pair in pairs
        if pair not in [position.pair for position in positions]
    ]
    for pair in pairs_not_in_position:
        row = df_list[pair].iloc[-2]
        for i in range(len(params[pair]["envelopes"])):
            tasks_open.append(
                exchange.place_trigger_order(
                    pair=pair,
                    side="buy",
                    price=exchange.price_to_precision(pair, row[f"ma_low_{i+1}"]),
                    trigger_price=exchange.price_to_precision(
                        pair, row[f"ma_low_{i+1}"] * 1.005
                    ),
                    size=(
                        (params[pair]["size"] * usdt_balance)
                        / len(params[pair]["envelopes"])
                        * size_leverage
                    )
                    / row[f"ma_low_{i+1}"],
                    type="limit",
                    reduce=False,
                    margin_mode=margin_mode,
                    error=False,
                )
            )
            tasks_open.append(
                exchange.place_trigger_order(
                    pair=pair,
                    side="sell",
                    trigger_price=exchange.price_to_precision(
                        pair, row[f"ma_high_{i+1}"] * 0.995
                    ),
                    price=exchange.price_to_precision(pair, row[f"ma_high_{i+1}"]),
                    size=(
                        (params[pair]["size"] * usdt_balance)
                        / len(params[pair]["envelopes"])
                        * size_leverage
                    )
                    / row[f"ma_high_{i+1}"],
                    type="limit",
                    reduce=False,
                    margin_mode=margin_mode,
                    error=False,
                )
            )

    print(f"Placing {len(tasks_open)} open limit order...")
    await asyncio.gather(*tasks_open)  # Limit orders when not in positions

    await exchange.close()
    print(f"--- Execution finished at {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ---")
except Exception as e:
    await exchange.close()
    raise e

if name == "main": while True: asyncio.run(main()) time.sleep(60)

zac667 commented 8 months ago

voici le code revisitée pour tous ceux que ça peut aider ... on est une Ekip <3