Repository: ccxt/ccxt (language: python)

Issue:
Kraken not parsing stop price correctly
- OS: Ubuntu 20.04
- Programming Language version: Python 3.8.10
- CCXT version: 1.89.19

Code:
```python
exch = ccxt.kraken({
    "enableRateLimit": True,
    "apiKey": key,
    "secret": secret,
})
o = exch.create_order(
    symbol="ETH/USD", 
    type="stop-loss-limit", 
    side="buy", 
    amount=0.01, 
    price=1160,
    params={"price2": 1170},
)
o = exch.fetch_order(o["id"])
print("Order info:", o["info"]["descr"]["order"])
print("Stop price (Should be 1160):", o["stopPrice"])
print("Limit price (Should be 1170):", o["price"])
```

Output:
```python
Order info: buy 0.01000000 ETHUSD @ stop loss 1160.00 -> limit 1170.00
Stop price (Should be 1160): 0.0
Limit price (Should be 1170): 1160.0
```

I've tracked down where the issue is occurring in the exchange source code. Initially, it actually gets the correct stop price from the order description:
https://github.com/ccxt/ccxt/blob/master/python/ccxt/kraken.py#L1343
A few lines down though, it overwrites that value with 0:
https://github.com/ccxt/ccxt/blob/master/python/ccxt/kraken.py#L1389

Below is the full order returned from calling `exch.fetch_order(id)`:
```python
{
    'info': {
        'id': '',
        'refid': None,
        'userref': '0',
        'status': 'open',
        'opentm': '1656542790.9874883',
        'starttm': '0',
        'expiretm': '0',
        'descr': {
            'pair': 'ETHUSD',
            'type': 'buy',
            'ordertype': 'stop-loss-limit',
            'price': '1160.00',
            'price2': '1170.00',
            'leverage': 'none',
            'order': 'buy 0.01000000 ETHUSD @ stop loss 1160.00 -> limit 1170.00',
            'close': ''
        },
        'vol': '0.01000000',
        'vol_exec': '0.00000000',
        'cost': '0.00000',
        'fee': '0.00000',
        'price': '0.00000',
        'stopprice': '0.00000',
        'limitprice': '0.00000',
        'misc': '',
        'oflags': 'fciq',
        'reason': None
    },
    'id': '',
    'clientOrderId': '0',
    'timestamp': 1656542790987,
    'datetime': '2022-06-29T22:46:30.987Z',
    'lastTradeTimestamp': None,
    'status': 'open',
    'symbol': 'ETH/USD',
    'type': 'stop-loss-limit',
    'timeInForce': None,
    'postOnly': None,
    'side': 'buy',
    'price': 1160.0,
    'stopPrice': 0.0,
    'cost': 0.0,
    'amount': 0.01,
    'filled': 0.0,
    'average': None,
    'remaining': 0.01,
    'fee': {'cost': 0.0, 'rate': None, 'currency': 'USD'},
    'trades': [],
    'fees': [{'cost': 0.0, 'rate': None, 'currency': 'USD'}]
}
```

You can see that although the order description states `"stop loss 1160.00"`, it also inexplicably has `'stopprice': '0.00000'`.



Discussion / hints:

Fix patch:
diff --git a/ts/src/kraken.ts b/ts/src/kraken.ts
index 749e28c0ac18..65ea9693e694 100644
--- a/ts/src/kraken.ts
+++ b/ts/src/kraken.ts
@@ -1513,7 +1513,7 @@ export default class kraken extends Exchange {
         //     {
         //         "error": [],
         //         "result": {
-        //             "descr": { order: 'buy 0.02100000 ETHUSDT @ limit 330.00' },
+        //             "descr": { order: 'buy 0.02100000 ETHUSDT @ limit 330.00' }, // see more examples in "parseOrder"
         //             "txid": [ 'OEKVV2-IH52O-TPL6GZ' ]
         //         }
         //     }
@@ -1581,9 +1581,10 @@ export default class kraken extends Exchange {
 
     parseOrderType (status) {
         const statuses: Dict = {
+            // we dont add "space" delimited orders here (eg. stop loss) because they need separate parsing
             'take-profit': 'market',
-            'stop-loss-limit': 'limit',
             'stop-loss': 'market',
+            'stop-loss-limit': 'limit',
             'take-profit-limit': 'limit',
             'trailing-stop-limit': 'limit',
         };
@@ -1592,30 +1593,19 @@ export default class kraken extends Exchange {
 
     parseOrder (order: Dict, market: Market = undefined): Order {
         //
-        // createOrder for regular orders
+        // createOrder
         //
         //     {
-        //         "descr": { order: 'buy 0.02100000 ETHUSDT @ limit 330.00' },
+        //         "descr": {
+        //            "order": "buy 0.02100000 ETHUSDT @ limit 330.00" // limit orders
+        //                     "buy 0.12345678 ETHUSDT @ market" // market order
+        //                     "sell 0.28002676 ETHUSDT @ stop loss 0.0123 -> limit 0.0.1222" // stop order
+        //                     "sell 0.00100000 ETHUSDT @ stop loss 2677.00 -> limit 2577.00 with 5:1 leverage"
+        //                     "buy 0.10000000 LTCUSDT @ take profit 75.00000 -> limit 74.00000"
+        //                     "sell 10.00000000 XRPEUR @ trailing stop +50.0000%" // trailing stop
+        //         },
         //         "txid": [ 'OEKVV2-IH52O-TPL6GZ' ]
         //     }
-        //     {
-        //         "txid": [ "TX_ID_HERE" ],
-        //         "descr": { "order":"buy 0.12345678 ETHEUR @ market" },
-        //     }
-        //
-        //
-        // createOrder for stop orders
-        //
-        //     {
-        //         "txid":["OSILNC-VQI5Q-775ZDQ"],
-        //         "descr":{"order":"sell 167.28002676 ADAXBT @ stop loss 0.00003280 -> limit 0.00003212"}
-        //     }
-        //
-        //
-        //     {
-        //         "txid":["OVHMJV-BZW2V-6NZFWF"],
-        //         "descr":{"order":"sell 0.00100000 ETHUSD @ stop loss 2677.00 -> limit 2577.00 with 5:1 leverage"}
-        //     }
         //
         // editOrder
         //
@@ -1694,26 +1684,32 @@ export default class kraken extends Exchange {
             orderDescription = this.safeString (order, 'descr');
         }
         let side = undefined;
-        let type = undefined;
+        let rawType = undefined;
         let marketId = undefined;
         let price = undefined;
         let amount = undefined;
-        let stopPrice = undefined;
+        let triggerPrice = undefined;
         if (orderDescription !== undefined) {
             const parts = orderDescription.split (' ');
             side = this.safeString (parts, 0);
             amount = this.safeString (parts, 1);
             marketId = this.safeString (parts, 2);
-            type = this.safeString (parts, 4);
-            if (type === 'stop') {
-                stopPrice = this.safeString (parts, 6);
+            const part4 = this.safeString (parts, 4);
+            const part5 = this.safeString (parts, 5);
+            if (part4 === 'limit' || part4 === 'market') {
+                rawType = part4; // eg, limit, market
+            } else {
+                rawType = part4 + ' ' + part5; // eg. stop loss, take profit, trailing stop
+            }
+            if (rawType === 'stop loss' || rawType === 'take profit') {
+                triggerPrice = this.safeString (parts, 6);
                 price = this.safeString (parts, 9);
-            } else if (type === 'limit') {
+            } else if (rawType === 'limit') {
                 price = this.safeString (parts, 5);
             }
         }
         side = this.safeString (description, 'type', side);
-        type = this.safeString (description, 'ordertype', type);
+        rawType = this.safeString (description, 'ordertype', rawType); // orderType has dash, e.g. trailing-stop
         marketId = this.safeString (description, 'pair', marketId);
         const foundMarket = this.findMarketByAltnameOrId (marketId);
         let symbol = undefined;
@@ -1775,15 +1771,29 @@ export default class kraken extends Exchange {
                 trades.push (rawTrade);
             }
         }
-        stopPrice = this.omitZero (this.safeString (order, 'stopprice', stopPrice));
+        // as mentioned in #24192 PR, this field is not something consistent/actual
+        // triggerPrice = this.omitZero (this.safeString (order, 'stopprice', triggerPrice));
         let stopLossPrice = undefined;
         let takeProfitPrice = undefined;
-        if (type.startsWith ('take-profit')) {
+        // the dashed strings are not provided from fields (eg. fetch order)
+        // while spaced strings from "order" sentence (when other fields not available)
+        if (rawType.startsWith ('take-profit')) {
             takeProfitPrice = this.safeString (description, 'price');
             price = this.omitZero (this.safeString (description, 'price2'));
-        } else if (type.startsWith ('stop-loss')) {
+        } else if (rawType.startsWith ('stop-loss')) {
             stopLossPrice = this.safeString (description, 'price');
             price = this.omitZero (this.safeString (description, 'price2'));
+        } else if (rawType === 'take profit') {
+            takeProfitPrice = triggerPrice;
+        } else if (rawType === 'stop loss') {
+            stopLossPrice = triggerPrice;
+        }
+        let finalType = this.parseOrderType (rawType);
+        // unlike from endpoints which provide eg: "take-profit-limit"
+        // for "space-delimited" orders we dont have market/limit suffixes, their format is
+        // eg: `stop loss > limit 123`, so we need to parse them manually
+        if (this.inArray (finalType, [ 'stop loss', 'take profit' ])) {
+            finalType = (price === undefined) ? 'market' : 'limit';
         }
         return this.safeOrder ({
             'id': id,
@@ -1794,13 +1804,13 @@ export default class kraken extends Exchange {
             'lastTradeTimestamp': undefined,
             'status': status,
             'symbol': symbol,
-            'type': this.parseOrderType (type),
+            'type': finalType,
             'timeInForce': undefined,
             'postOnly': isPostOnly,
             'side': side,
             'price': price,
-            'stopPrice': stopPrice,
-            'triggerPrice': stopPrice,
+            'stopPrice': triggerPrice,
+            'triggerPrice': triggerPrice,
             'takeProfitPrice': takeProfitPrice,
             'stopLossPrice': stopLossPrice,
             'cost': undefined,


Test patch:
diff --git a/ts/src/test/static/response/kraken.json b/ts/src/test/static/response/kraken.json
index 213112cc3a74..fb946232f04e 100644
--- a/ts/src/test/static/response/kraken.json
+++ b/ts/src/test/static/response/kraken.json
@@ -60,6 +60,67 @@
                   "lastUpdateTimestamp": null,
                   "reduceOnly": null
                 }
+            },
+            {
+                "description": "spot limit buy stopLossPrice",
+                "method": "createOrder",
+                "input": [
+                  "LTC/USDT",
+                  "limit",
+                  "buy",
+                  0.1,
+                  30,
+                  {
+                    "stopLossPrice": 29
+                  }
+                ],
+                "httpResponse": {
+                  "error": [],
+                  "result": {
+                    "txid": [
+                      "OJZ42L-YACUJ-YCYN4C"
+                    ],
+                    "descr": {
+                      "order": "buy 0.10000000 LTCUSDT @ stop loss 29.00000 -> limit 30.00000"
+                    }
+                  }
+                },
+                "parsedResponse": {
+                  "id": "OJZ42L-YACUJ-YCYN4C",
+                  "clientOrderId": null,
+                  "info": {
+                    "txid": [
+                      "OJZ42L-YACUJ-YCYN4C"
+                    ],
+                    "descr": {
+                      "order": "buy 0.10000000 LTCUSDT @ stop loss 29.00000 -> limit 30.00000"
+                    }
+                  },
+                  "timestamp": null,
+                  "datetime": null,
+                  "lastTradeTimestamp": null,
+                  "status": null,
+                  "symbol": "LTC/USDT",
+                  "type": "limit",
+                  "timeInForce": null,
+                  "postOnly": false,
+                  "side": "buy",
+                  "price": 30,
+                  "stopPrice": 29,
+                  "triggerPrice": 29,
+                  "takeProfitPrice": null,
+                  "stopLossPrice": 29,
+                  "cost": null,
+                  "amount": 0.1,
+                  "filled": null,
+                  "average": null,
+                  "remaining": null,
+                  "reduceOnly": null,
+                  "fee": null,
+                  "trades": [],
+                  "fees": [],
+                  "lastUpdateTimestamp": null
+                }
             }
         ],
         "fetchOrder": [
@@ -382,8 +443,8 @@
                         "postOnly": false,
                         "side": "sell",
                         "price": null,
-                        "stopPrice": null,
-                        "triggerPrice": null,
+                        "stopPrice": 120,
+                        "triggerPrice": 120,
                         "takeProfitPrice": 120,
                         "stopLossPrice": null,
                         "cost": null,
@@ -489,8 +550,8 @@
                         "postOnly": false,
                         "side": "sell",
                         "price": 54,
-                        "stopPrice": null,
-                        "triggerPrice": null,
+                        "stopPrice": 55,
+                        "triggerPrice": 55,
                         "takeProfitPrice": null,
                         "stopLossPrice": 55,
                         "cost": 0,