Skip to content

Commit

Permalink
Added Transaction and Instrument Subcommands (#3)
Browse files Browse the repository at this point in the history
* Update Readme.md

* Updated to get only refresh from code

* cargo fmt and minor alignment tdameritradeclient

* working on instruments subcommand

* finished instrument and started transaction

* finished adding transaction history
  • Loading branch information
jbertovic authored Aug 24, 2020
1 parent bf2a26a commit 619e9bb
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 76 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
[package]
name = "tdacli"
version = "0.2.1"
version = "0.2.2"
authors = ["Jas Bertovic <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
#tdameritradeclient = { path = "../tdameritradeclient" }
tdameritradeclient = { git = "https://github.com/jbertovic/tdameritradeclient.git", tag = "v0.2.1" }
tdameritradeclient = { git = "https://github.com/jbertovic/tdameritradeclient.git", tag = "v0.2.2" }
clap = "2.33"
9 changes: 5 additions & 4 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
## TDACLI

A simple CLI wrapper around my tdameritradeclient library. For help enter `tdacli --help`. This will give you access to help and further subcommands.
A simple CLI wrapper around my tdameritradeclient library. For help enter `tdacli --help`. This will give you access to help and further subcommand help categories.

Current subcommands are: quote, history, optionchain, userprincipals, account, auth, refresh, weblink. See below for description on output of `tdaci --help` in the CLI Commands section
Current subcommands are: userprincipals, account, quote, history, optionchain, instrument, transaction, auth, refresh, weblink. See below for description on output of `tdaci --help` in the CLI Commands section

Environmental Variable Requirements:
- `TDAUTHTOKEN` on subcommands: account, history, optionchain, quote, userprincipals
Expand Down Expand Up @@ -105,9 +105,11 @@ SUBCOMMANDS:
auth Retrieves refresh_token using authorization_code grant type
help Prints this message or the help of the given subcommand(s)
history Retrieve history for one <symbol>.
instrument Retrieve instrument information or search for instrument
optionchain Retrieve option chain for one <symbol>
quote Retrieve quotes for requested symbols
refresh Fetch valid token or renew refresh_token using a refresh_token grant type
transaction Retrieve transaction history
userprincipals Retrieves User Principals
weblink Gives you the url to get authorization code from TDAmeritrade
Expand Down Expand Up @@ -156,5 +158,4 @@ and the period as the term or total length of the history.

- [ ] add option for date calculations on History epoch data stamps
- [ ] Consider adding date conversion using chrono package
- [ ] orders - adding, deleting, listing
- [ ] potential to integrate a json query language on output
- [ ] orders - a way to handle orders with library adding and deleting
51 changes: 42 additions & 9 deletions src/account.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,66 @@
use clap::ArgMatches;
use tdameritradeclient::{TDAClient, Account};
use tdameritradeclient::{Account, TDAClient, Transactions};

/// Grabs the userprincipals data from tdameritrade
/// calls `tdameritradeclient::getuserprincipals()`
pub fn userprincipals(c: &TDAClient) {
let resp: String = c.getuserprincipals();
println!("{}", &resp);
}

/// Grabs the account information with options for Positions, Orders or both
/// calls `tdameritradeclient::getaccount(account_id, Account param)
pub fn account(c: &TDAClient, args: &ArgMatches) {
match args.value_of("account_id") {
Some(account) => {
let resp: String;
if args.is_present("positions")&&args.is_present("orders") {
if args.is_present("positions") && args.is_present("orders") {
resp = c.getaccount(&account, &[Account::PositionsAndOrders]);
}
else if args.is_present("positions") {
} else if args.is_present("positions") {
resp = c.getaccount(&account, &[Account::Positions]);
}
else if args.is_present("orders") {
} else if args.is_present("orders") {
resp = c.getaccount(&account, &[Account::Orders]);
}
else {
} else {
resp = c.getaccount(&account, &[]);
}
println!("{}", resp)
}
None => println!("{{ \"error\": \"Missing account_id\" }}"),
}
}
/// Grabs transaction detail with options for date range, or one transaction id
/// or restricted to symbol or type
/// calls `tdameritradeclient::transactions(account_id, transaction param)
/// or calls `tdameritradeclient::transaction(account_id, transaction_id)
pub fn transaction(c: &TDAClient, args: &ArgMatches) {
match args.value_of("account_id") {
Some(account) => {
let resp: String;
// if transaction_id is supplied than just grab that transaction and
// ignore all other options
if args.is_present("trans_id") {
resp = c.gettransaction(&account, args.value_of("trans_id").unwrap());
} else {
let mut param: Vec<Transactions> = Vec::new();
if args.is_present("transaction_type") {
param.push(Transactions::TransactionType(
args.value_of("transaction_type").unwrap(),
));
}
if args.is_present("symbol") {
param.push(Transactions::Symbol(args.value_of("symbol").unwrap()));
}
if args.is_present("start_date") {
param.push(Transactions::StartDate(
args.value_of("start_date").unwrap(),
));
}
if args.is_present("end_date") {
param.push(Transactions::EndDate(args.value_of("end_date").unwrap()));
}
resp = c.gettransactions(&account, &param);
}
println!("{}", resp)
}
None => println!("{{ \"error\": \"Missing account_id\" }}"),
}
}
33 changes: 19 additions & 14 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
use clap::ArgMatches;
use tdameritradeclient::auth::{getcodeweblink, gettoken_fromrefresh, getrefresh_fromrefresh, TDauth};
use tdameritradeclient::auth::{
getcodeweblink, getrefresh_fromrefresh, gettoken_fromrefresh, TDauth,
};

/// Create a link to use to get an authorization_code from tdameritrade
/// Code can be used to fetch a token and refresh token in the `auth` subcommand
pub fn weblink(args: &ArgMatches) {
println!("{}",
getcodeweblink(args.value_of("clientid").unwrap(), args.value_of("redirect").unwrap()));
println!(
"{}",
getcodeweblink(
args.value_of("clientid").unwrap(),
args.value_of("redirect").unwrap()
)
);
}
/// Fetch updated `token` or `refresh_token` from a current `refresh_token`
/// Fetch updated `refresh_token` from a current `authorization_code` as retrieved from `weblink`
pub fn auth(args: &ArgMatches, code: String) {
match args.value_of("clientid") {
Some(clientid) => {
match args.value_of("redirect") {
Some(redirect) => {
let decoded = !args.is_present("decoded");
let tdauth = TDauth::new_fromcode(&code, clientid, redirect, decoded);
let (t, r) = tdauth.gettokens();
println!("{{\"Token\": \"{}\", \"Refresh\": \"{}\"}}", t, r)
},
None => println!("{{ \"error\": \"Missing redirect\"}}"),
Some(clientid) => match args.value_of("redirect") {
Some(redirect) => {
let decoded = !args.is_present("decoded");
let tdauth = TDauth::new_fromcode(&code, clientid, redirect, decoded);
let (_t, refresh) = tdauth.gettokens();
println!("{}", refresh);
}
None => println!("{{ \"error\": \"Missing redirect\"}}"),
},
None => println!("{{ \"error\": \"Missing clientid\"}}"),
}
Expand All @@ -39,4 +44,4 @@ pub fn refresh(args: &ArgMatches, rtoken: String) {
}
None => println!("{{ \"error\": \"Missing clientid\"}}"),
}
}
}
74 changes: 72 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub fn cli_matches<'a>() -> ArgMatches<'a> {
.help("includes account orders")
)
.after_help("NOTE: Token must be set in env variable: TDAUTHTOKEN.")
)
)
.subcommand(
SubCommand::with_name("quote")
.about("Retrieve quotes for requested symbols")
Expand All @@ -91,7 +91,77 @@ pub fn cli_matches<'a>() -> ArgMatches<'a> {
.help("Retrieves quotes of supplied <symbols> in format \"sym1,sym2,sym3\""
))
.after_help("NOTE: Token must be set in env variable: TDAUTHTOKEN.")
)
)
.subcommand(
SubCommand::with_name("transaction")
.about("Retrieve transaction history")
.arg(
Arg::with_name("account_id")
.takes_value(true)
.required(true)
.help("Retrieves transactions for linked account_id")
)
.arg( // symbol shouldn't be an argument with value but ONLY the value
Arg::with_name("transaction_type")
.long("type")
.takes_value(true)
.help("Specify Transaction Type otherwise ALL (See below)")
)
.arg( // symbol shouldn't be an argument with value but ONLY the value
Arg::with_name("trans_id")
.help("(OPTIONAL) Grab one transaction by ID. Ignores all other options.")
)
.arg( // symbol shouldn't be an argument with value but ONLY the value
Arg::with_name("symbol")
.long("symbol")
.takes_value(true)
.help("Specify symbol, otherwise all symbols")
)
.arg( // symbol shouldn't be an argument with value but ONLY the value
Arg::with_name("start_date")
.long("sdate")
.takes_value(true)
.help("Start date in yyyy-mm-dd. Max range is 1 year")
)
.arg( // symbol shouldn't be an argument with value but ONLY the value
Arg::with_name("end_date")
.long("edate")
.takes_value(true)
.help("End date in yyyy-mm-dd. Max range is 1 year")
)
.after_help("NOTE: Token must be set in env variable: TDAUTHTOKEN. \n\n\r\
Transaction Types: ALL, TRADE, BUY_ONLY, SELL_ONLY, CASH_IN_OR_CASH_OUT, CHECKING, \n\
DIVIDEND, INTEREST, OTHER, ADVISOR_FEES")
)
.subcommand(
SubCommand::with_name("instrument")
.about("Retrieve instrument information or search for instrument")
.arg( // symbol shouldn't be an argument with value but ONLY the value
Arg::with_name("search")
.required(true)
.takes_value(true)
.help("Symbol of instrument or search parameter")
)
.arg( // symbol shouldn't be an argument with value but ONLY the value
Arg::with_name("search_type")
.required(true)
.takes_value(true)
.help("Specifies type of request")
)
.after_help("NOTE: Token must be set in env variable: TDAUTHTOKEN. \n\r\n\r\
Type of Request \n\r\
- symbol-search: Retrieve instrument data of a specific symbol or cusip \n\r\
- symbol-regex: Retrieve instrument data for all symbols matching regex. \n\r\
Example: search = XYZ.* will return all symbols beginning with XYZ \n\r\
- desc-search: Retrieve instrument data for instruments whose description \n\r\
contains the word supplied. Example: search = FakeCompany will return \n\r\
all instruments with FakeCompany in the description. \n\r\
- desc-regex: Search description with full regex support. \n\r\
Example: search = XYZ.[A-C] returns all instruments whose descriptions \n\r\
contain a word beginning with XYZ followed by a character A through C. \n\r\
- fundamental: Returns fundamental data for a single instrument specified by exact symbol.\n\r\
- cusip: use the supplied cusip to look up details of instrument.")
)
.subcommand(
SubCommand::with_name("history")
.about("Retrieve history for one <symbol>.")
Expand Down
34 changes: 16 additions & 18 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
//!
//! module -> subcommands that match tdameritrade::TDAClient
//!
//! `account -> userprinicipals(), account(account_id)`
//! `account -> userprinicipals(), account(account_id), transactions(account_id), transaction(account_id, transaction_id)`
//! Defines items that deal with account and user information
//!
//! `quote -> quote(symbols), history(symbol, History param), optionchain(symbol, OptionChain param)`
//! -> instruments(search), instrument(cusip)
//! Defines items that deal with quotes either live or historical and expanded option quotes
//!
//! `orders` -> getsavedorders(accoundid), getorders(accountid)`
Expand All @@ -19,26 +20,20 @@
#[macro_use(crate_version)]
extern crate clap;

pub mod cli;
pub mod account;
pub mod quote;
pub mod auth;
pub mod cli;
pub mod quote;

use std::env;
use tdameritradeclient::TDAClient;

fn main() {
let matches = cli::cli_matches();

//TODO: add orders subcommand
//TODO: orders: output filled, working, all
//TODO: orders: add (need to determine how to specify order desc json)
//TODO: orders: delete

match matches.subcommand() {
(cmd, Some(sub_m)) => {
match cmd {

// relies on NO Env Variables
"weblink" => auth::weblink(&sub_m),

Expand All @@ -47,31 +42,34 @@ fn main() {
let refresh = env::var("TDREFRESHTOKEN")
.expect("Token is missing inside env variable TDREFRESHTOKEN");
auth::refresh(sub_m, refresh);
}
}

// relies on env variable TDCODE
"auth" => {
let code = env::var("TDCODE")
.expect("Code is missing inside env variable TDCODE");
let code =
env::var("TDCODE").expect("Code is missing inside env variable TDCODE");
auth::auth(sub_m, code);
}
}

// relies on env variable TDAUTHTOKEN and tdameritradeclient::TDClient
_ => {
let c = TDAClient::new(env::var("TDAUTHTOKEN")
.expect("Token is missing inside env variable TDAUTHTOKEN"));
let c = TDAClient::new(
env::var("TDAUTHTOKEN")
.expect("Token is missing inside env variable TDAUTHTOKEN"),
);
match cmd {
"userprincipals" => account::userprincipals(&c),
"account" => account::account(&c, &sub_m),
"quote" => quote::quote(&c, sub_m),
"transaction" => account::transaction(&c, sub_m),
"instrument" => quote::instrument(&c, sub_m),
"history" => quote::history(&c, sub_m),
"optionchain" => quote::optionchain(&c, sub_m),
_ => {},
_ => {}
}
}
}
}
_ => {println!("Subcommand must be specified. For more information try --help")}
_ => println!("Subcommand must be specified. For more information try --help"),
}
}

Loading

0 comments on commit 619e9bb

Please sign in to comment.