Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add create_bracket_order() #14

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Once you've obtained the key and secret you can authenticate manually with

```julia
using AlpacaMarkets
AlpacaMarkets.select_account("PAPER")
AlpacaMarkets.trading_env("PAPER")
AlpacaMarkets.auth(KEY, SECRET)
```

Expand Down Expand Up @@ -49,13 +49,12 @@ So you can pull some historical data as and when needed.

Trading API functions are available up to account, orders and positions:

* `account`, `get_orders`, `place_order`, `place_market_order`
* `place_limit_order`, `place_stop_order`, `place_stop_limit_order`,
* `place_trailing_stop_order`, `place_bracket_order`,
* `place_oco_order`, `place_oto_order`, `replace_an_order`,
* `cancel_order`, `cancel_all_orders`, `get_orders_by_order_id`
* `get_orders_by_client_order_id`, `get_open_positions`,
* `get_position`, `close_all_positions`, `close_position`
* `account`, `create_market_order`, `create_limit_order`, `create_stop_order`
* `create_stop_limit_order`, `create_trailing_stop_order`, `create_bracket_order`,
* `get_orders`, `get_orders_by_order_id`, `get_orders_by_client_order_id`,
* `cancel_all_orders`, `cancel_order`, `replace_an_order`,
* `close_all_positions`, `close_position`, `get_all_positions`
* `get_position`

## To Do

Expand Down
62 changes: 43 additions & 19 deletions src/orders.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
function create_order_params(;symbol, side, qty=NaN, notional=NaN, time_in_force, type, extended_hours=false, client_order_id="none")

validate_order(side, time_in_force, qty, notional)

params = Dict("symbol" => symbol,
"side" => side,
"time_in_force" => time_in_force,
"type" => type,
"extended_hours" => extended_hours)
function create_order_params(;symbol, side, qty=NaN, notional=NaN, time_in_force, type, extended_hours=false, client_order_id="none", tp_limit_price=nothing, sl_stop_price=nothing, sl_limit_price=nothing,order_class=nothing)

validate_order(side, time_in_force, qty, notional, order_class, type)

if isnothing(order_class)
params = Dict("symbol" => symbol,
"side" => side,
"time_in_force" => time_in_force,
"type" => type,
"extended_hours" => extended_hours)
elseif !isnothing(order_class)
params = Dict("symbol" => symbol,
"side" => side,
"time_in_force" => time_in_force,
"type" => type,
"extended_hours" => extended_hours,
"order_class" => order_class,
"take_profit" => Dict("limit_price" => tp_limit_price),
"stop_loss" => Dict("stop_price" => sl_stop_price, "limit_price" => sl_limit_price))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error is thrown as these aren't passed into the function.
They don't need to be passed into the function, the tp/sl params are used in the bracket function.
Also duplication of code

Copy link
Contributor Author

@flare9x flare9x Jul 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will check - intention was if order_class=nothing it was the normal orders up to this point and if it was specified then would revert to the ifelse statement in the function. order_class="bracket" along with an assertion in utils : @assert order_class in ["simple", "bracket", "oco ", "oto"] "valid order class :: simple, bracket, oco, oto". i did this so that we could do a dict within dict for the tp,sl of these order types. i will verify what happened here. ok i realized where the error was solved see below.

Copy link
Contributor Author

@flare9x flare9x Jul 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create_order_params() repaired and now should be able to extend to "bracket", "oco ", "oto"

Copy link
Contributor Author

@flare9x flare9x Jul 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

create_bracket_order(symbol="AAPL", side = "buy", qty = 1, time_in_force = "day", type = "limit", limit_price = 196.00, tp_limit_price=196.10, sl_stop_price=194.00, sl_limit_price = 193.60)

for brackets tested a few ways of executing the 'parent' order - market, limit and stop limit.

enter with limit order using bracket.

image

and if wish to enter on a stop limit bracket order::

create_bracket_order(symbol="AAPL", side = "buy", qty = 1, time_in_force = "day", type = "stop_limit", limit_price = 196.00, stop_price = 196.10, tp_limit_price=196.50, sl_stop_price=194.00, sl_limit_price = 193.60)

image

  "qty"            => 1
  "extended_hours" => false
  "limit_price"    => 196.0
  "symbol"         => "AAPL"
  "stop_loss"      => Dict("limit_price"=>193.6, "stop_price"=>194.0)
  "take_profit"    => Dict("limit_price"=>196.5)
  "order_class"    => "bracket"
  "stop_price"     => 196.1
  "side"           => "buy"
  "time_in_force"  => "day"
  "type"           => "stop_limit"

end

if client_order_id != "none"
params["client_order_id"] = client_order_id
Expand All @@ -31,36 +42,35 @@ end

function create_market_order(;symbol, side, qty=NaN, notional=NaN, time_in_force, extended_hours=false, client_order_id="none")
params = create_order_params(symbol=symbol, side=side, qty=qty, notional=notional, time_in_force = time_in_force, type = "market",
extended_hours=extended_hours, client_order_id=client_order_id)
extended_hours=extended_hours, client_order_id=client_order_id, order_class=nothing)
submit_order(params)
end

function create_limit_order(;symbol, side, qty=NaN, notional=NaN, limit_price, time_in_force, extended_hours=false, client_order_id="none")
params = create_order_params(symbol=symbol, side=side, qty=qty, notional=notional, time_in_force = time_in_force, type = "limit",
extended_hours=extended_hours, client_order_id=client_order_id)
extended_hours=extended_hours, client_order_id=client_order_id, order_class=nothing)
params["limit_price"] = limit_price
submit_order(params)
end

function create_stop_order(;symbol, side, qty=NaN, notional=NaN, stop_price, time_in_force, extended_hours=false, client_order_id="none")
params = create_order_params(symbol=symbol, side=side, qty=qty, notional=notional, time_in_force = time_in_force, type = "stop",
extended_hours=extended_hours, client_order_id=client_order_id)
extended_hours=extended_hours, client_order_id=client_order_id, order_class=nothing)
params["stop_price"] = stop_price
submit_order(params)
end

function create_stop_limit_order(;symbol, side, qty=NaN, notional=NaN, limit_price, stop_price, time_in_force, extended_hours=false, client_order_id="none")
params = create_order_params(symbol=symbol, side=side, qty=qty, notional=notional, time_in_force = time_in_force, type = "stop_limit",
extended_hours=extended_hours, client_order_id=client_order_id)
extended_hours=extended_hours, client_order_id=client_order_id, order_class=nothing)
params["stop_price"] = stop_price
params["limit_price"] = limit_price
submit_order(params)
end

function create_trailing_stop_order(;symbol, side, qty=NaN, notional=NaN, trail_price=NaN, trail_percent=NaN, time_in_force, extended_hours=false, client_order_id="none")

params = create_order_params(symbol=symbol, side=side, qty=qty, notional=notional, time_in_force = time_in_force, type = "stop_limit",
extended_hours=extended_hours, client_order_id=client_order_id)
extended_hours=extended_hours, client_order_id=client_order_id, order_class=nothing)
validate_size(trail_price, trail_percent)
if !isnan(trail_price)
params["trail_price"] = trail_price
Expand All @@ -73,8 +83,22 @@ function create_trailing_stop_order(;symbol, side, qty=NaN, notional=NaN, trail_
submit_order(params)
end

function create_bracket_order()
#nyi
function create_bracket_order(;symbol, side, type, qty=NaN, notional=NaN, limit_price=NaN, stop_price=NaN, tp_limit_price, sl_stop_price, sl_limit_price, time_in_force, extended_hours=false, client_order_id="none")
params = create_order_params(symbol=symbol, side=side, qty=qty, notional=notional, time_in_force = time_in_force, type = type, extended_hours=extended_hours, tp_limit_price=tp_limit_price, sl_stop_price=sl_stop_price,
sl_limit_price=sl_limit_price, client_order_id=client_order_id, order_class="bracket")

if !isnan(limit_price)
params["limit_price"] = limit_price
end

if !isnan(stop_price)
params["stop_price"] = stop_price
end

params["take_profit"]["limit_price"] = tp_limit_price
params["stop_loss"]["stop_price"] = sl_stop_price
params["stop_loss"]["limit_price"] = sl_limit_price
submit_order(params)
end

function create_oco_order()
Expand Down Expand Up @@ -141,7 +165,7 @@ function cancel_all_orders()::DataFrame
return resdf
end

function cancel_order(order_id::String)
function cancel_order(order_id::String)::DataFrame
url = join([TRADING_API_URL, "orders", order_id], "/")
res = HTTP.delete(url, headers = HEADERS[])
resdict = JSON.parse(String(res.body))
Expand All @@ -150,7 +174,7 @@ function cancel_order(order_id::String)
end


function replace_an_order(;order_id::String, params::Dict)
function replace_an_order(;order_id::String, params::Dict)::DataFrame

url = join([TRADING_API_URL, "orders", order_id], "/")

Expand Down
9 changes: 0 additions & 9 deletions src/positions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,6 @@ function close_position(symbol::String; qty=NaN, percentage=NaN)::DataFrame
return resdf
end

function get_positions(symbol::String)::DataFrame
url = join([TRADING_API_URL, "positions", symbol], "/")

res = HTTP.get(url, headers = HEADERS[])
resdict = JSON.parse(String(res.body))
resdf = DataFrame(resdict)
return resdf
end

function get_positions()::DataFrame
url = join([TRADING_API_URL, "positions"], "/")

Expand Down
19 changes: 16 additions & 3 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,29 @@ function validate_side(side::String)
end

function validate_size(qty, notional)
@assert sum(isnan.([qty, notional])) == 1
@assert sum(isnan.([qty, notional])) == 1 "Either qty or notational - can not state both"
true
end

function validate_type(type::String)
@assert type in ["market", "limit", "stop", "stop_limit", "trailing_stop"] "valid types :: market, limit, stop, stop_limit, or trailing_stop"
true
end

function validate_order(side::String, tif::String, qty, notional)
function validate_class(order_class::Any)
if isnothing(order_class)
order_class = "simple"
end
@assert order_class in ["simple", "bracket", "oco ", "oto"] "valid order class :: simple, bracket, oco, oto"
true
end

function validate_order(side::String, tif::String, qty, notional, order_class, type::String)
validate_side(side)
validate_tif(tif)
validate_size(qty, notional)
validate_type(type)
validate_class(order_class)
true
end


7 changes: 5 additions & 2 deletions test/orders_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@

@test params["notional"] == 100
#@test !(["qty", "client_order_id"] in keys(params))


params = AlpacaMarkets.create_order_params(symbol="AAPL", side = "buy", qty = 1, time_in_force = "day", type = "market", tp_limit_price=196.10, sl_stop_price=194.00, sl_limit_price = 193.60, order_class = "bracket")

@test params["order_class"] == "bracket"

end

end
end