Custom Orders
This section demonstrates how to implement an OCO (One-Cancels-the-Other) order type for simulation purposes:
using OrderTypes: OrderType, @deforders
abstract type OCOOrderType{S} <: OrderType{S}
@deforders OCO
We can base our implementation on the existing constructor for limit orders and modify it to meet the requirements of an OCO order:
const _OCOOrderState = NamedTuple{(:committed, :filled, :trades, :twin), Tuple{Vector{Float64}, Vector{Float64}, Vector{Trade}, Ref{OCOOrder}}}
function oco_order_state(
committed::Vector{T}, filled::Vector{Float64}=[0.0], trades::Vector{Trade}=Vector{Trade}()
) where T
_OCOOrderState((committed, filled, trades, Ref{OCOOrder}()))
end
function ocoorder(
ai::AssetInstance,
::SanitizeOff;
price_lower::Float64,
amount_lower::Float64,
price_upper::Float64,
amount_upper::Float64,
committed_lower::Vector{Float64},
committed_upper::Vector{Float64},
date::Datetime
)
ismonotonic(price_lower, price_upper) || return nothing
iscost(ai, amount_lower, price_lower) || return nothing
iscost(ai, amount_upper, price_upper) || return nothing
lower_order = OrderTypes.Order(
ai,
OCOOrderType{Sell};
date,
price_lower,
amount_lower,
committed_lower,
attrs=oco_order_state(committed_lower)
)
upper_order = OrderTypes.Order(
ai,
OCOOrderType{Buy};
date,
price_upper,
amount_upper,
committed_upper,
attrs=oco_order_state(committed_upper)
)
lower_order.attrs[:twin] = upper_order
upper_order.attrs[:twin] = lower_order
return lower_order
end
Next, we introduce two call!
functions to handle creating and updating simulated OCO orders:
@doc "Creates a simulated OCO order."
function call!(
s::Strategy{Sim}, ::Type{Order{<:OCOOrderType}}, ai; date, kwargs...
)
o = ocoorder(s, ai; date, kwargs...)
isnothing(o) && return nothing
iscommittable(s, o, ai) || return nothing
# TODO: Implement logic to execute the order and return resulting trades.
end
@doc "Updates a simulated OCO order."
function call!(
s::Strategy{Sim}, ::Type{<:Order{OCOOrderType}}, date::Datetime, ai; kwargs...
)
o = ocoorder(s, ai; date, kwargs...)
isnothing(o) && return nothing
iscommittable(s, o, ai) || return nothing
iscommittable(s, o.attrs[:twin], ai) || return nothing
# TODO: Implement logic to execute the order update and return resulting trades.
end
Custom Instruments
We can extend instruments to create new types such as Asset
and Derivative
, which are subtypes of AbstractAsset
. They are named using the CCXT convention (QUOTE/BASE:SETTLE
), and it's expected that all instruments define a base and a quote currency.
Instances and Exchanges
Asset instances are parameterized by the type of the asset (e.g., asset, derivative) and the exchange they are associated with. By using ExchangeID
as a parameter, we can fine-tune the behavior for specific exchanges.
For example, if we want to handle OCO orders differently across exchanges in live mode, we can define call!
functions that are specialized based on the exchange parameter of the asset instance.
function call!(
s::Strategy{Live},
::Type{Order{<:OCOOrderType}},
ai::AssetInstance{A, ExchangeID{:bybit}};
date,
kwargs...
)
# Replace the following comment with the actual call to a private method of the ccxt exchange class to execute the order.
### Call some private method of the ccxt exchange class to execute the order
end
The function above is designed to handle asset instances that are specifically tied to the bybit
exchange.