Skip to content

Commit

Permalink
Merge pull request #85 from ssoper/features/revamped-client
Browse files Browse the repository at this point in the history
Simplify client creation
  • Loading branch information
ssoper committed Dec 21, 2021
2 parents 91aa947 + 4b0a320 commit 0f48191
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 44 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -28,7 +28,7 @@ repositories {
}

dependencies {
implementation("com.seansoper:batil:1.0.1")
implementation("com.seansoper:batil:1.0.2")
}
```

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
@@ -1,5 +1,5 @@
group = "com.seansoper"
version = "1.0.1"
version = "1.0.2"

/**
* To publish
Expand Down
10 changes: 5 additions & 5 deletions src/main/kotlin/Samples.kt
Expand Up @@ -136,7 +136,7 @@ class Accounts {
val service = Accounts(session, runtime.production, runtime.verbose)

service.list()?.let {
it.first().accountIdKey?.let { accountIdKey ->
it.first().accountIdKey.let { accountIdKey ->
service.viewPortfolio(accountIdKey)?.let {
println("View portfolio")
println(it)
Expand All @@ -152,7 +152,7 @@ class Accounts {
val service = Accounts(session, runtime.production, runtime.verbose)

service.list()?.let {
it.first().accountIdKey?.let { accountIdKey ->
it.first().accountIdKey.let { accountIdKey ->
service.listTransactions(accountIdKey, sortOrder = TransactionSortOrder.DESC, count = 5)?.let {
println("List five most recent transactions")
println(it)
Expand All @@ -168,7 +168,7 @@ class Accounts {
val service = Accounts(session, runtime.production, runtime.verbose)

service.list()?.let {
it.first().accountIdKey?.let { accountIdKey ->
it.first().accountIdKey.let { accountIdKey ->
service.listTransactions(accountIdKey, sortOrder = TransactionSortOrder.DESC, count = 1)?.let {
service.getTransaction(accountIdKey, it.transactions.first().transactionId)?.let {
println("Most recent transaction")
Expand All @@ -192,7 +192,7 @@ class Orders {
val accounts = Accounts(session, runtime.production, runtime.verbose)

accounts.list()?.let {
it.first().accountIdKey?.let { accountIdKey ->
it.first().accountIdKey.let { accountIdKey ->
val service = Orders(session, runtime.production, runtime.verbose)
service.list(accountIdKey)?.let {
println("Orders for account $accountIdKey")
Expand Down Expand Up @@ -435,7 +435,7 @@ class Orders {
val accounts = Accounts(session, runtime.production, runtime.verbose)

accounts.list()?.let {
it.first().accountIdKey?.let { accountIdKey ->
it.first().accountIdKey.let { accountIdKey ->
val service = Orders(session, runtime.production, runtime.verbose)
val previewRequest = buyEquityLimit("PLTR", 21f, 1)

Expand Down
44 changes: 44 additions & 0 deletions src/main/kotlin/brokers/etrade/EtradeClient.kt
@@ -0,0 +1,44 @@
package com.seansoper.batil.brokers.etrade

import com.seansoper.batil.brokers.etrade.auth.Authorization
import com.seansoper.batil.brokers.etrade.auth.Session
import com.seansoper.batil.brokers.etrade.services.Accounts
import com.seansoper.batil.brokers.etrade.services.Alerts
import com.seansoper.batil.brokers.etrade.services.Market
import com.seansoper.batil.brokers.etrade.services.Orders

// TODO: Replace verbose with logger interface

class EtradeClient(
val key: String,
val secret: String,
val username: String,
val password: String,
val endpoint: Endpoint,
val verbose: Boolean = false
) {

enum class Endpoint(val url: String) {
SANDBOX("https://apisb.etrade.com"),
LIVE("https://api.etrade.com")
}

private val production = endpoint == Endpoint.LIVE

private val authorization = Authorization(
key = key,
secret = secret,
username = username,
password = password,
production = production,
verbose = verbose,
baseUrl = endpoint.url
)

private val session: Session = authorization.renewSession() ?: authorization.createSession()

val accounts = Accounts(session, production = production, verbose = verbose)
val market = Market(session, production = production, verbose = verbose)
val alerts = Alerts(session, production = production, verbose = verbose)
val orders = Orders(session, production = production, verbose = verbose)
}
79 changes: 43 additions & 36 deletions src/main/kotlin/brokers/etrade/auth/Authorization.kt
Expand Up @@ -5,36 +5,43 @@ import com.seansoper.batil.CachedTokenException
import com.seansoper.batil.CachedTokenProvider
import com.seansoper.batil.brokers.etrade.interceptors.HttpInterceptor
import com.seansoper.batil.brokers.etrade.interceptors.OauthKeys
import com.seansoper.batil.config.Chromium
import com.seansoper.batil.config.DefaultChromium
import com.seansoper.batil.config.GlobalConfig
import okhttp3.OkHttpClient
import okhttp3.Request

// TODO: Line up production/verbose argument order with ClientConfig
// FIXME: Production flag is doing too much, just take a base url or use the Endpoint enum
class Authorization(
private val configuration: GlobalConfig,
private val production: Boolean = false,
private val verbose: Boolean = false,
private val baseUrl: String = "https://${(if (production) "api" else "apisb")}.etrade.com",
private val tokenStore: CachedTokenProvider = CachedToken(CachedToken.Provider.ETRADE)
val key: String,
val secret: String,
val username: String,
val password: String,
val production: Boolean = false,
val verbose: Boolean = false,
val baseUrl: String,
val chromium: Chromium = DefaultChromium,
val tokenStore: CachedTokenProvider = CachedToken(CachedToken.Provider.ETRADE)
) {

private val consumerKey: String
get() {
return if (production) {
configuration.etrade.production.key
} else {
configuration.etrade.sandbox.key
}
}

private val consumerSecret: String
get() {
return if (production) {
configuration.etrade.production.secret
} else {
configuration.etrade.sandbox.secret
}
}
constructor(
configuration: GlobalConfig,
production: Boolean = false,
verbose: Boolean = false,
baseUrl: String = "https://${(if (production) "api" else "apisb")}.etrade.com",
tokenStore: CachedTokenProvider = CachedToken(CachedToken.Provider.ETRADE)
) : this(
key = if (production) { configuration.etrade.production.key } else { configuration.etrade.sandbox.key },
secret = if (production) { configuration.etrade.production.secret } else { configuration.etrade.sandbox.secret },
username = configuration.etrade.username,
password = configuration.etrade.password,
production = production,
verbose = verbose,
baseUrl = baseUrl,
chromium = configuration.chromium,
tokenStore = tokenStore
)

private val paths: HashMap<String, String> = hashMapOf(
GET_REQUEST_TOKEN to "oauth/request_token",
Expand Down Expand Up @@ -72,8 +79,8 @@ class Authorization(

fun getRequestToken(): AuthResponse {
val keys = OauthKeys(
consumerKey = consumerKey,
consumerSecret = consumerSecret
consumerKey = key,
consumerSecret = secret
)

val client = OkHttpClient.Builder()
Expand All @@ -90,11 +97,11 @@ class Authorization(

fun getVerifierCode(token: String): String {
val browserAuth = BrowserAuthentication(
consumerKey,
key,
token,
configuration.etrade.username,
configuration.etrade.password,
configuration.chromium,
username,
password,
chromium,
verbose
)

Expand All @@ -103,8 +110,8 @@ class Authorization(

fun getAccessToken(requestToken: AuthResponse, verifier: String): AuthResponse {
val keys = OauthKeys(
consumerKey = consumerKey,
consumerSecret = consumerSecret,
consumerKey = key,
consumerSecret = secret,
accessToken = requestToken.accessToken,
accessSecret = requestToken.accessSecret,
verifier = verifier
Expand Down Expand Up @@ -138,8 +145,8 @@ class Authorization(

fun renewAccessToken(requestToken: AuthResponse): Boolean {
val keys = OauthKeys(
consumerKey = consumerKey,
consumerSecret = consumerSecret,
consumerKey = key,
consumerSecret = secret,
accessToken = requestToken.accessToken,
accessSecret = requestToken.accessSecret
)
Expand All @@ -159,8 +166,8 @@ class Authorization(

fun revokeAccessToken(requestToken: AuthResponse): Boolean {
val keys = OauthKeys(
consumerKey = consumerKey,
consumerSecret = consumerSecret,
consumerKey = key,
consumerSecret = secret,
accessToken = requestToken.accessToken,
accessSecret = requestToken.accessSecret
)
Expand Down Expand Up @@ -191,7 +198,7 @@ class Authorization(
tokenStore.setEntry(CACHED_KEY_CODE, verifier)
}

Session(consumerKey, consumerSecret, it.accessToken, it.accessSecret, verifier)
Session(key, secret, it.accessToken, it.accessSecret, verifier)
}
}

Expand All @@ -200,7 +207,7 @@ class Authorization(
val requestToken = AuthResponse(it.first, it.second)

if (renewAccessToken(requestToken)) {
Session(consumerKey, consumerSecret, it.first, it.second, it.third)
Session(key, secret, it.first, it.second, it.third)
} else {
null
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/brokers/etrade/services/Service.kt
Expand Up @@ -15,6 +15,8 @@ import retrofit2.converter.jackson.JacksonConverterFactory
import java.text.SimpleDateFormat
import java.util.GregorianCalendar

// TODO: Remove duplicative arguments like production, verbose and baseUrl since Session should hold all of these

open class Service(
session: Session,
_production: Boolean?,
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/config/EtradeConfig.kt
@@ -1,5 +1,7 @@
package com.seansoper.batil.config

// TODO: Bifurcate this config like Alpaca client does, have username + password and then live flag

data class EtradeConfig(
val sandbox: EtradeAuth,
val production: EtradeAuth,
Expand Down
7 changes: 6 additions & 1 deletion src/main/kotlin/config/GlobalConfig.kt
Expand Up @@ -8,10 +8,15 @@ import java.nio.file.Files

// TODO: Consider modifying the parse method to just take a path and showing the Jackson error regardless of verbose
// TODO: Add a constructor that takes a ClientConfig
// TODO: Add options to take values as is or path to file, use live endpoint
// TODO: Session should expose high level APIs
// TODO: No more passing verbose/production thru every call

val DefaultChromium = Chromium("127.0.0.1", port = 9222, delay = 5)

data class GlobalConfig(
val etrade: EtradeConfig,
val chromium: Chromium = Chromium("127.0.0.1", port = 9222, delay = 5)
val chromium: Chromium = DefaultChromium
) {
companion object {
fun parse(clientConfig: ClientConfig): GlobalConfig {
Expand Down

0 comments on commit 0f48191

Please sign in to comment.