Skip to content

Commit c06576d

Browse files
author
Fernando Fernandes
committed
Add offset business logic + logger
1 parent c812487 commit c06576d

File tree

5 files changed

+130
-9
lines changed

5 files changed

+130
-9
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// Double+Decimal.swift
3+
//
4+
//
5+
// Created by Fernando Fernandes on 11.02.22.
6+
//
7+
8+
import Foundation
9+
10+
public extension Double {
11+
12+
/// Returns the decimal value of `self` **without** scientific notation (e.g.: "1.2e-6").
13+
func toDecimalString() -> String {
14+
"\(NSNumber(value: Double(self)).decimalValue)"
15+
}
16+
17+
/// Returns the decimal numbers of `self`.
18+
///
19+
/// Examples:
20+
/// - 43865.52: returns "2"
21+
/// - 0.934: returns "3"
22+
/// - 0.00003456: returns "8"
23+
/// - 2: returns "0"
24+
func decimalCount() -> Int {
25+
if self == Double(Int(self)) {
26+
return 0
27+
}
28+
let integerString = "\(NSNumber(value: Int(self)).decimalValue)"
29+
let doubleString = Double(self).toDecimalString()
30+
let decimalCount = doubleString.count - integerString.count - 1
31+
return decimalCount
32+
}
33+
}

Sources/SwiftTrader/Network/NetworkRequest/NetworkRequestLogger.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public struct NetworkRequestLogger {
1717

1818
// MARK: - Lifecycle
1919

20-
public init(label: String = "com.backslash-f.network") {
20+
public init(label: String = "com.backslash-f.swift-trader.network") {
2121
self.default = Logger(label: label)
2222
}
2323
}

Sources/SwiftTrader/SwiftTrader+Offset.swift

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,74 @@ import Foundation
1515
public extension SwiftTrader {
1616

1717
func createOrderParameters(for input: SwiftTraderOrderInput) -> KucoinOrderParameters {
18-
let targetPercentage: Double = (input.profitPercentage / 100) - (input.offset / 100)
18+
logger.log("Creating order parameters...")
19+
20+
// E.g: 7.47 -> 0.0747
21+
let profitPercentage: Double = (input.profitPercentage / 100)
22+
logger.log("Profit percentage: \(profitPercentage.toDecimalString())")
23+
24+
// Instead of creating an order with a fixed target percentage (say 0.025), add some fat to it (e.g.: 0.027)
25+
let offsetMargin: Double = 0.02
26+
logger.log("Offset margin: \(offsetMargin.toDecimalString())")
27+
28+
// E.g.: (0.75 - 0.02) / 100 = 0.0073, which translates to 0.0027 (0,27%)
29+
let offset: Double = (input.offset - offsetMargin) / 100
30+
logger.log("Offset: \(offset.toDecimalString())")
31+
32+
// E.g.: (0.0747 - 0.0073) = 0.0674 (6,74%)
33+
let targetPercentage: Double = profitPercentage - offset
34+
logger.log("Target percentage: \(targetPercentage.toDecimalString())")
35+
36+
// E.g.: 42000.69 * 0.0674 = 2830.846506
1937
let priceIncrement: Double = input.entryPrice * targetPercentage
38+
logger.log("Price increment: \(priceIncrement.toDecimalString())")
39+
40+
// E.g.: 42000.9 + 2830.846506 = 44831.536506
41+
let targetPrice: Double = input.entryPrice + priceIncrement
42+
logger.log("Entry price: \(input.entryPrice.toDecimalString())")
43+
logger.log("Target price: \(targetPrice.toDecimalString())")
44+
45+
// Now in order to avoid a huge difference between the entry price and the target price in terms of size,
46+
// "normalize" them based on the entry price by first counting its characters.
47+
let entryPriceCount = input.entryPrice.toDecimalString().count
48+
logger.log("Entry price count: \(entryPriceCount)")
49+
50+
// Count the target price characters too.
51+
let targetPriceCount = targetPrice.toDecimalString().count
52+
logger.log("Target price count: \(targetPriceCount)")
53+
54+
// E.g.: 12 - 8 (44831.536506 - 42000.69)
55+
// "44831.536506" becomes "44831.53".
56+
let charactersToBeRemoved = abs(targetPriceCount - entryPriceCount)
57+
var targetPriceString = "\(targetPrice.toDecimalString())".dropLast(charactersToBeRemoved)
58+
logger.log("Target price normalize: \(targetPriceString)")
59+
60+
// Finally, avoid the following Kucoin error with minimal effort: "The parameter shall be a multiple of ..."
61+
// First, just try replacing the last character by "1". E.g.: "0.00002347" becomes "0.00002341"
62+
if targetPriceString.components(separatedBy: ".").count > 1 {
63+
targetPriceString = targetPriceString.dropLast() + "1"
64+
} else {
65+
// Handles whole numbers: "3735" becomes "3735.1" (instead of "3731").
66+
targetPriceString += ".1"
67+
}
68+
logger.log("Target price string: \(targetPriceString)")
2069

21-
#warning("TODO: round up?")
22-
// Why Int? From a Kucoin response: "The parameter shall be a multiple of 1."
23-
let targetPrice = Int(input.entryPrice + priceIncrement)
24-
let targetPriceString = String(targetPrice)
70+
// E.g.: "44831.53" becomes "44831".
71+
// Workaround to not call "https://docs.kucoin.com/futures/#get-open-contract-list" (for now).
72+
// "The price specified must be a multiple number of the contract tickSize, otherwise the system will report an
73+
// error when you place the order. The tick size is the smallest price increment in which the prices are quoted.
74+
if let priceDouble = Double(targetPriceString), priceDouble > 10 {
75+
targetPriceString = "\(priceDouble.rounded(.down).toDecimalString())"
76+
}
2577

2678
return KucoinOrderParameters(
2779
symbol: input.contractSymbol,
2880
side: .sell,
2981
type: .limit,
3082
stop: .down,
3183
stopPriceType: .TP,
32-
stopPrice: targetPriceString,
33-
price: targetPriceString,
84+
stopPrice: "\(targetPriceString)",
85+
price: "\(targetPriceString)",
3486
reduceOnly: true,
3587
closeOrder: true
3688
)

Sources/SwiftTrader/SwiftTrader.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,21 @@
66
//
77

88
import Foundation
9+
import Logging
910

1011
/// Entry point for connecting and trading on crypto exchanges such as Binance and Kucoin.
1112
public struct SwiftTrader {
1213

1314
// MARK: - Properties
1415

15-
private let settings: SwiftTraderSettings
16+
public let logger = SwiftTraderLogger()
1617

1718
// MARK: Private
1819

1920
private let kucoinAuth: KucoinAuth
2021

22+
private let settings: SwiftTraderSettings
23+
2124
// MARK: - Lifecycle
2225

2326
public init(kucoinAuth: KucoinAuth, settings: SwiftTraderSettings = DefaultSwiftTraderSettings()) {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// SwiftTraderLogger.swift
3+
//
4+
//
5+
// Created by Fernando Fernandes on 11.02.22.
6+
//
7+
8+
import Foundation
9+
import Logging
10+
11+
/// Logs information via Apple's `swift-log` package.
12+
public struct SwiftTraderLogger {
13+
14+
// MARK: - Private Properties
15+
16+
private let logger: Logger
17+
18+
// MARK: - Lifecycle
19+
20+
public init(label: String = "com.backslash-f.swift-trader") {
21+
self.logger = Logger(label: label)
22+
}
23+
}
24+
25+
// MARK: - Interface
26+
27+
public extension SwiftTraderLogger {
28+
29+
func log(_ message: Logger.Message) {
30+
logger.log(level: .debug, message)
31+
}
32+
}
33+

0 commit comments

Comments
 (0)