Skip to content

Commit

Permalink
Merge pull request #8 from mautomic/hist_candles
Browse files Browse the repository at this point in the history
price history
  • Loading branch information
mautomic authored May 31, 2021
2 parents 5acb109 + 7bf2278 commit 2f55acd
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 3 deletions.
6 changes: 6 additions & 0 deletions src/main/java/com/sleet/api/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ object Constants {
const val EQUALS = '='
const val GRANT_TYPE = "grant_type"
const val MARKETDATA = "marketdata"
const val PRICEHISTORY = "pricehistory"
const val OFFLINE = "offline"
const val ORDERS = "orders"
const val REDIRECT_URI = "redirect_uri"
Expand All @@ -31,6 +32,7 @@ object Constants {
const val SLASH = "/"
const val TOKEN_ENDPOINT = "oauth2/token"
const val URL_ENCODED = "application/x-www-form-urlencoded"
const val QUESTION_MARK = "?"

const val QUERY_PARAM_OTM = "&range=OTM"
const val QUERY_PARAM_CONTRACT_TYPE = "&contractType="
Expand All @@ -44,4 +46,8 @@ object Constants {
const val QUERY_PARAM_TO_ENTERED_TIME = "&toEnteredTime="
const val QUERY_PARAM_ORDERS_AND_POSITIONS = "?fields=positions%2Corders"
const val QUERY_PARAM_USER_PRINCIPALS = "userprincipals?fields=streamerSubscriptionKeys%2CstreamerConnectionInfo"
const val QUERY_PARAM_PERIOD_TYPE = "&periodType="
const val QUERY_PARAM_PERIOD = "&period="
const val QUERY_PARAM_FREQUENCY_TYPE = "&frequencyType="
const val QUERY_PARAM_FREQUENCY = "&frequency="
}
8 changes: 8 additions & 0 deletions src/main/java/com/sleet/api/model/Candles.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.sleet.api.model

import com.fasterxml.jackson.annotation.JsonIgnoreProperties

@JsonIgnoreProperties(ignoreUnknown = true)
class Candles {
val candles: Array<PriceHistory> = emptyArray()
}
13 changes: 13 additions & 0 deletions src/main/java/com/sleet/api/model/PriceHistory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.sleet.api.model

import com.fasterxml.jackson.annotation.JsonIgnoreProperties

@JsonIgnoreProperties(ignoreUnknown = true)
class PriceHistory {
val open: Double = 0.0
val high: Double = 0.0
val low: Double = 0.0
val close: Double = 0.0
val volume: Int = 0
val datetime: String? = null
}
60 changes: 57 additions & 3 deletions src/main/java/com/sleet/api/service/QuoteService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import com.fasterxml.jackson.databind.JsonNode
import com.sleet.api.util.RequestUtil.Companion.createGetRequest
import com.sleet.api.Constants
import com.sleet.api.Constants.DEFAULT_TIMEOUT_MILLIS
import com.sleet.api.model.Asset
import com.sleet.api.model.Contract
import com.sleet.api.model.OptionChain
import com.sleet.api.model.*
import org.asynchttpclient.AsyncHttpClient
import org.asynchttpclient.Response
import org.slf4j.LoggerFactory
Expand All @@ -33,6 +31,8 @@ class QuoteService(
) {

private var OPTION_CHAIN_URL: String = Constants.API_URL + Constants.MARKETDATA + "/chains?apikey=" + apiKey
private var HIST_PRICE_URL: String = Constants.API_URL + Constants.MARKETDATA
private var HIST_PRICE_API: String = "/pricehistory?apikey=$apiKey"
private var QUOTE_URL: String = "/quotes?apikey=$apiKey"

companion object {
Expand Down Expand Up @@ -324,6 +324,43 @@ class QuoteService(
return getOptionChainForStrikeAsync(ticker, strike)[DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS]
}

/**
* Queries the TD API endpoint for historical prices for a specified period & frequency
*
* @param ticker of security to retrieve prices for
* @param periodType type of period to show, e.g. [day, month, year, ytd]
* @param period number of periods to show
* @param frequencyType type of frequency with which a new candle is formed, e.g. [minute, daily, weekly, monthly]
* @param frequency the number of frequencies to be included
* @param startDate start date as milliseconds since epoch
* @param endDate end date as milliseconds since epoch
* @return [Response] with all option data for the ticker
*/
@Throws(Exception::class)
fun getPriceHistory(ticker: String?, periodType: String?, period: String?, frequencyType: String?, frequency: String?,
startDate: String?, endDate: String?): CompletableFuture<Candles?> {

val builder = StringBuilder()
.append(HIST_PRICE_URL)
.append(Constants.SLASH)
.append(ticker)
.append(HIST_PRICE_API)
.append(Constants.QUERY_PARAM_PERIOD_TYPE)
.append(periodType)
.append(Constants.QUERY_PARAM_PERIOD)
.append(period)
.append(Constants.QUERY_PARAM_FREQUENCY_TYPE)
.append(frequencyType)
.append(Constants.QUERY_PARAM_FREQUENCY)
.append(frequency)

val future = CompletableFuture<Candles?>()
val request = createGetRequest(builder.toString(), null)
httpClient.executeRequest(request).toCompletableFuture()
.whenComplete { resp: Response, _: Throwable? -> future.complete(deserializeHistoryResponse(resp)) }
return future
}

/**
* Queries the TD API endpoint asynchronously for all options for a ticker with a specific strike price
*
Expand Down Expand Up @@ -439,6 +476,23 @@ class QuoteService(
return null
}

/**
* Deserialize a JSON response string into an [PriceHistory]
*
* @param response to deserialize
* @return [PriceHistory] for the original request, or null if exception occurs
*/
private fun deserializeHistoryResponse(response: Response): Candles? {
if (response.statusCode == 200) {
try {
return mapper.readValue(response.responseBody, Candles::class.java)
} catch (e: Exception) {
logFailure(e)
}
}
return null
}

/**
* Method for logging exceptions after failed HTTP requests. Exit program
* if api key expires.
Expand Down
14 changes: 14 additions & 0 deletions src/test/java/com/sleet/api/service/QuoteServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,18 @@ class QuoteServiceTest {
Thread.sleep(2000)
}
}


@Test
@Ignore
@Throws(Exception::class)
fun testHistoricalPrices() {
val quoteService = QuoteService(TestConstants.API_KEY, Dsl.asyncHttpClient(Dsl.config()))
val time = System.currentTimeMillis()
val priceHistory = quoteService.getPriceHistory("SPY", "month", "1", "daily", "1", "", "")
println("Retrieval for price history " + (System.currentTimeMillis() - time) + " ms")

val history = priceHistory.get()
Assert.assertNotNull(history)
}
}

0 comments on commit 2f55acd

Please sign in to comment.