diff --git a/Sources/CoinGecko/Models/Candle.swift b/Sources/CoinGecko/Models/Candle.swift new file mode 100644 index 0000000..8c14088 --- /dev/null +++ b/Sources/CoinGecko/Models/Candle.swift @@ -0,0 +1,34 @@ +// +// Candle.swift +// +// +// Created by stringcode on 05/04/2021. +// + +import Foundation + +public typealias CandleList = [Candle] + +public struct Candle: Codable { + public let date: Date + public let open: Double + public let high: Double + public let low: Double + public let close: Double + + public init(arrayData: [Double]) { + date = Date(timeIntervalSince1970: Double(arrayData[0]) / 1000) + open = arrayData[1] + high = arrayData[2] + low = arrayData[3] + close = arrayData[4] + } + + public init(date: Date, open: Double, high: Double, low: Double, close: Double) { + self.date = date + self.open = open + self.high = high + self.low = low + self.close = close + } +} diff --git a/Sources/CoinGecko/Resources.swift b/Sources/CoinGecko/Resources.swift index 706d6ac..97c2a07 100644 --- a/Sources/CoinGecko/Resources.swift +++ b/Sources/CoinGecko/Resources.swift @@ -15,7 +15,8 @@ public enum Endpoint: String { case coinsList = "/coins/list" case coinsMarketChart = "/coins/%@/market_chart" - case coin = "/coins/%@" + case coin = "/coins/%@" + case ohlc = "/coins/%@/ohlc" } public enum Resources {} @@ -81,4 +82,23 @@ extension Resources { URLQueryItem(name: "days", value: "\(days)")] return Resource(.coinsMarketChart, method: .GET, pathParam: currencyId, params: params, completion: callback) } + + // Coin Gecko API returns candles of diffrent duration based on `days` parameter + // ``` + // 1 - 2 days: 30 minutes + // 3 - 30 days: 4 hours + // 31 and before: 4 days + // ``` + public static func candles(currencyId: String, vs: String, days: Int, callback: @escaping Callback) -> Resource { + let params = [URLQueryItem(name: "vs_currency", value: vs), + URLQueryItem(name: "days", value: "\(days)")] + + let parse: (Data) -> CandleList = { data in + let decoder = JSONDecoder() + let arrayData = try? decoder.decode([[Double]].self, from: data) + return (arrayData ?? []).map { Candle(arrayData: $0) } as! CandleList + } + + return Resource(.ohlc, method: .GET, pathParam: currencyId, params: params, parse: parse, completion: callback) + } } diff --git a/Tests/CoinGeckoTests/CandleTest.swift b/Tests/CoinGeckoTests/CandleTest.swift new file mode 100644 index 0000000..290b2d3 --- /dev/null +++ b/Tests/CoinGeckoTests/CandleTest.swift @@ -0,0 +1,25 @@ +import XCTest +@testable import CoinGecko + +final class CandleTests: XCTestCase { + + private let client = CoinGeckoClient() + + func testCandles() { + let exp = XCTestExpectation() + let id = "bitcoin" + let vs = "usd" + let candles = Resources.candles(currencyId: id, vs: vs, days: 180) { (result: Result) in + switch result { + case let .success(candleList): + XCTAssertTrue(candleList.count > 44 && candleList.count < 49, + "Unexpected candle count") + case let .failure(error): + XCTFail(error.localizedDescription) + } + exp.fulfill() + } + client.load(candles) + wait(for: [exp], timeout: 10.0) + } +}