@@ -14,7 +14,7 @@ import Foundation
14
14
/// such instance is based on a given `SwiftTraderOrderInput`/`offset`.
15
15
public extension SwiftTrader {
16
16
17
- func createOrderParameters( for input: SwiftTraderOrderInput ) -> KucoinOrderParameters {
17
+ func createOrderParameters( for input: SwiftTraderOrderInput ) throws -> KucoinOrderParameters {
18
18
logger. log ( " Creating order parameters... " )
19
19
20
20
// E.g: 7.47 -> 0.0747
@@ -31,50 +31,73 @@ public extension SwiftTrader {
31
31
32
32
// E.g.: (0.0747 - 0.0073) = 0.0674 (6,74%)
33
33
// Use "abs" to filter out negative numbers.
34
- let targetPercentage : Double = abs ( profitPercentage - offset)
34
+ let targetPercentage : Double = profitPercentage - offset
35
35
logger. log ( " Target percentage: \( targetPercentage. toDecimalString ( ) ) " )
36
36
37
37
// E.g.: 42000.69 * 0.0674 = 2830.846506
38
38
let priceIncrement : Double = input. entryPrice * targetPercentage
39
39
logger. log ( " Price increment: \( priceIncrement. toDecimalString ( ) ) " )
40
40
41
- // E.g.: 42000.9 + 2830.846506 = 44831.536506
41
+ // E.g.: 42000.69 + 2830.846506 = 44831.536506
42
42
let targetPrice : Double = input. entryPrice + priceIncrement
43
43
logger. log ( " Entry price: \( input. entryPrice. toDecimalString ( ) ) " )
44
44
logger. log ( " Target price: \( targetPrice. toDecimalString ( ) ) " )
45
45
46
- // Now in order to avoid a huge difference between the entry price and the target price in terms of size,
47
- // "normalize" them based on the entry price by first counting its characters.
48
- let entryPriceCount = input. entryPrice. toDecimalString ( ) . count
49
- logger. log ( " Entry price count: \( entryPriceCount) " )
46
+ var targetPriceString = " \( targetPrice. toDecimalString ( ) ) "
50
47
51
- // Count the target price characters too.
52
- let targetPriceCount = targetPrice. toDecimalString ( ) . count
53
- logger. log ( " Target price count: \( targetPriceCount) " )
54
-
55
- // E.g.: 12 - 8 (44831.536506 - 42000.69)
56
- // "44831.536506" becomes "44831.53".
57
- let charactersToBeRemoved = abs ( targetPriceCount - entryPriceCount)
58
- var targetPriceString = " \( targetPrice. toDecimalString ( ) ) " . dropLast ( charactersToBeRemoved)
59
- logger. log ( " Target price normalized: \( targetPriceString) " )
48
+ // Finally, handle the following requirement:
49
+ //
50
+ // "The price specified must be a multiple number of the contract tickSize, otherwise the system will report an
51
+ // error when you place the order. The tick size is the smallest price increment in which the prices are quoted.
52
+ guard let tickerSizeDouble = Double ( input. tickerSize) ,
53
+ let priceDouble = Double ( targetPriceString) else {
54
+ throw SwiftTraderError . kucoinCouldNotCalculateTheTargetPrice ( input: input)
55
+ }
60
56
61
- // Finally, avoid the following Kucoin error with minimal effort: "The parameter shall be a multiple of ..."
62
- // First, just try replacing the last character by "1". E.g.: "0.00002347" becomes "0.00002341"
63
- if targetPriceString. components ( separatedBy: " . " ) . count > 1 {
64
- targetPriceString = targetPriceString. dropLast ( ) + " 1 "
57
+ // Whole ticker size like "1", "2", etc.
58
+ if tickerSizeDouble. truncatingRemainder ( dividingBy: 1 ) == 0 {
59
+ // E.g.: "44831.53" becomes "44832".
60
+ targetPriceString = " \( priceDouble. rounded ( . up) . toDecimalString ( ) ) "
61
+
65
62
} else {
66
- // Handles whole numbers: "3735" becomes "3735.1" (instead of "3731").
67
- targetPriceString += " .1 "
63
+ // Handle ticker sizes like "0.0001", "0.05", "0.00000001", etc.
64
+ //
65
+ // E.g., considering "0.0001":
66
+ // - "0.023" becomes "0.0231"
67
+ // - "0.0456" becomes "0.0451"
68
+ // - "0.7" becomes "0.7001"
69
+ guard let targetPriceLastDigit = targetPriceString. last,
70
+ let tickerLastDigit = input. tickerSize. last else {
71
+ throw SwiftTraderError . kucoinCouldNotCalculateTheTargetPrice ( input: input)
72
+ }
73
+
74
+ let tickerDigits = input. tickerSize. decimalCount ( )
75
+ let targetPriceDigits = targetPriceString. decimalCount ( )
76
+
77
+ if targetPriceDigits == tickerDigits, targetPriceLastDigit != tickerLastDigit {
78
+ targetPriceString = targetPriceString. dropLast ( ) + " \( tickerLastDigit) "
79
+
80
+ } else if targetPriceDigits > tickerDigits {
81
+ let digitsToRemove = ( targetPriceDigits - tickerDigits) + 1
82
+ targetPriceString = targetPriceString. dropLast ( digitsToRemove) + " \( tickerLastDigit) "
83
+
84
+ } else if targetPriceDigits < tickerDigits {
85
+ let digitsToAdd = ( tickerDigits - targetPriceDigits)
86
+ let digits : [ String ] = Array ( repeating: " 0 " , count: digitsToAdd)
87
+ targetPriceString = targetPriceString + digits. joined ( separator: " " )
88
+ targetPriceString = targetPriceString. dropLast ( ) + " \( tickerLastDigit) "
89
+ }
68
90
}
91
+
92
+ logger. log ( " Ticker size: \( input. tickerSize) " )
69
93
logger. log ( " Target price string: \( targetPriceString) " )
70
94
71
- // E.g.: "44831.53" becomes "44831".
72
- // Workaround to not call "https://docs.kucoin.com/futures/#get-open-contract-list" (for now).
73
- // "The price specified must be a multiple number of the contract tickSize, otherwise the system will report an
74
- // error when you place the order. The tick size is the smallest price increment in which the prices are quoted.
75
- if let priceDouble = Double ( targetPriceString) , priceDouble > 10 {
76
- targetPriceString = " \( priceDouble. rounded ( . down) . toDecimalString ( ) ) "
77
- logger. log ( " Target price string: \( targetPriceString) " )
95
+ // Throw in case the target price became lower than the entry price for whatever reason.
96
+ // Do not place an order in this scenario.
97
+ guard Double ( targetPriceString) ?? 0 > input. entryPrice else {
98
+ throw SwiftTraderError . kucoinInvalidTargetPrice (
99
+ entryPrice: input. entryPrice. toDecimalString ( ) ,
100
+ targetPrice: targetPriceString)
78
101
}
79
102
80
103
return KucoinOrderParameters (
0 commit comments