/ OrderTypes / src / module.jl
module.jl
  1  using ExchangeTypes
  2  import ExchangeTypes: exchangeid
  3  using Instruments
  4  using Instruments: Misc
  5  import Base: ==
  6  
  7  using .Misc: config, PositionSide, Long, Short, TimeTicks, Lang
  8  using .Misc.DocStringExtensions
  9  import .Misc: opposite
 10  using .TimeTicks
 11  using .ExchangeTypes: Exchange
 12  
 13  @doc """ Abstract type representing an event in an exchange
 14  Types implementing an `ExchangeEvent` must have a `tag::Symbol` field.
 15  Every instance of such event should have a unique tag and an optional group.
 16  """
 17  abstract type ExchangeEvent{E} end
 18  
 19  function event!(
 20      exc::Exchange,
 21      kind::Type{<:ExchangeEvent},
 22      tag,
 23      group;
 24      event_date=now(),
 25      this_date=now(),
 26      kwargs...,
 27  )
 28      ev = kind{exc.id}(Symbol(tag), Symbol(group), NamedTuple(k => v for (k, v) in kwargs))
 29      push!(exc._trace, ev; event_date, this_date)
 30  end
 31  
 32  function event!(exc::Exchange, ev::ExchangeEvent; event_date=now(), this_date=now())
 33      push!(exc._trace, ev; event_date, this_date)
 34  end
 35  
 36  @doc """Records an event in the exchange's trace. """
 37  event!(v, args...; kwargs...) = event!(exchange(v), args...; kwargs...)
 38  
 39  struct AssetEvent{E} <: ExchangeEvent{E}
 40      tag::Symbol
 41      group::Symbol
 42      data::NamedTuple
 43  end
 44  struct StrategyEvent{E} <: ExchangeEvent{E}
 45      tag::Symbol
 46      group::Symbol
 47      data::NamedTuple
 48  end
 49  
 50  @doc """ Abstract type representing the side of an order """
 51  abstract type OrderSide end
 52  @doc """ Abstract type representing the buy side of an order """
 53  abstract type Buy <: OrderSide end
 54  @doc """ Abstract type representing the sell side of an order """
 55  abstract type Sell <: OrderSide end
 56  @doc """ Abstract type representing both sides of an order """
 57  abstract type BuyOrSell <: OrderSide end
 58  
 59  @doc """ Abstract type representing the type of an order """
 60  abstract type OrderType{S<:OrderSide} end
 61  @doc """ Abstract type representing any limit order """
 62  abstract type LimitOrderType{S} <: OrderType{S} end
 63  @doc """ Abstract type representing any immediate order """
 64  abstract type ImmediateOrderType{S} <: LimitOrderType{S} end
 65  @doc """ Abstract type representing GTC (good till cancel) orders """
 66  abstract type GTCOrderType{S} <: LimitOrderType{S} end
 67  @doc """ Abstract type representing post only orders"""
 68  abstract type PostOnlyOrderType{S} <: GTCOrderType{S} end
 69  @doc """ Abstract type representing FOK (fill or kill) orders """
 70  abstract type FOKOrderType{S} <: ImmediateOrderType{S} end
 71  @doc """ Abstract type representing IOC (immediate or cancel) orders """
 72  abstract type IOCOrderType{S} <: ImmediateOrderType{S} end
 73  @doc """ Abstract type representing market orders """
 74  abstract type MarketOrderType{S} <: OrderType{S} end
 75  @doc """ Abstract type representing liquidation orders """
 76  abstract type LiquidationType{S} <: MarketOrderType{S} end
 77  @doc """ Abstract type representing forced orders """
 78  abstract type ForcedOrderType{S} <: MarketOrderType{S} end
 79  
 80  @doc """An Order is a container for trades, tied to an asset and an exchange.
 81  Its execution depends on the order implementation.
 82  
 83  $(FIELDS)
 84  
 85  `date`: the time at which the strategy requested the order.
 86      The strategy is assumed to have *knowledge* of the ohlcv data \
 87      strictly lower than the timeframe adjusted date.
 88      Example:
 89      ```julia
 90      ts = dt"2020-05-24T02:34:00" # the date of the order request
 91      tf = @infertf ohlcv # get the timeframe (15m)
 92      start_date = ohlcv.timestamp[begin]
 93      stop_date = apply(tf, ts) # normalize date to timeframe
 94      stop_date -= tf.period # scale down by one timeframe step
 95      # At this point the stop date would be `2020-05-24T02:30:00`
 96      # which covers the period between ...02:30:00..02:45:00...
 97      # Therefore the strategy can only have access to data < 02:30:00
 98      avail_ohlcv = ohlcv[DateRange(start_date, stop_date), :]
 99      @assert isless(avail_ohlcv.timestamp[end], dt"2020-05-24T02:30:00")
100      @assert isequal(avail_ohlcv.timestamp[end] + tf.period, dt"2020-05-24T02:30:00")
101      ```
102  """
103  struct Order{
104      T<:OrderType{S} where {S<:OrderSide},A<:AbstractAsset,E<:ExchangeID,P<:PositionSide
105  }
106      asset::A
107      exc::E
108      date::DateTime
109      price::Float64
110      amount::Float64
111      id::String
112      tag::String
113      attrs::NamedTuple
114  end
115  
116  @doc """ Creates an Order object.
117  
118  $(TYPEDSIGNATURES)
119  
120  This function constructs an Order object with the given parameters.
121  The Order object represents a trade order tied to an asset and an exchange.
122  The execution of the order depends on the order implementation.
123  The function takes in parameters for the asset, exchange, order type, position side, price, date, amount, attributes, and an optional id.
124  
125  """
126  function Order(
127      a::A,
128      e::E,
129      ::Type{Order{T}},
130      ::Type{P}=Long;
131      price,
132      date,
133      amount,
134      attrs=(;),
135      id="",
136      tag="",
137      kwargs...,
138  ) where {T<:OrderType,A<:AbstractAsset,E<:ExchangeID,P<:PositionSide}
139      Order{T,A,E,P}(a, e, date, price, amount, id, tag, attrs)
140  end
141  function Order(
142      a, e, ::Type{Order{T,<:AbstractAsset,<:ExchangeID,P}}; kwargs...
143  ) where {T<:OrderType,P<:PositionSide}
144      Order(a, e, Order{T}, P; kwargs...)
145  end
146  Base.hash(o::Order{T}) where {T} = hash((T, o.asset, o.exc, o.date, o.price, o.amount))
147  function Base.hash(o::Order{T}, h::UInt) where {T}
148      hash((T, o.asset, o.exc, o.date, o.price, o.amount), h)
149  end
150  @doc """ Compares two Order objects based on their date.
151  
152  $(TYPEDSIGNATURES)
153  
154  This function compares the date of two Order objects and returns `true` if the date of the first Order is less than the date of the second Order. It is used to sort or compare Orders based on their date.
155  
156  """
157  Base.isless(o1::O1, o2::O2) where {O1,O2<:Order} = isless(o1.date, o2.date)
158  @doc """ Get the fees of an order.
159  
160  $(TYPEDSIGNATURES)
161  
162  This function returns the total fees of an order, which is the sum of the fees of all trades associated with the order.
163  """
164  fees(o::Order) =
165      let tds = trades(o)
166          if isempty(tds)
167              nothing
168          else
169              sum(fees(t) for t in tds)
170          end
171      end
172  
173  @doc "An order that increases the size of a position."
174  const BuyOrder{A,E} = Order{<:OrderType{Buy},A,E,Long}
175  @doc "An order that decreases the size of a position."
176  const SellOrder{A,E} = Order{<:OrderType{Sell},A,E,Long}
177  @doc "A type representing any order that involves buying, regardless of the specific order type or position side"
178  const AnyBuyOrder{P,A,E} = Order{<:OrderType{Buy},A,E,P}
179  @doc "A type representing any order that involves selling, regardless of the specific order type or position side"
180  const AnySellOrder{P,A,E} = Order{<:OrderType{Sell},A,E,P}
181  @doc "A type representing an order that opens or adds to a 'long' position in a specific asset"
182  const LongOrder{O,A,E} = Order{O,A,E,Long}
183  @doc "A type representing an order that opens or adds to a 'short' position in a specific asset"
184  const ShortOrder{O,A,E} = Order{O,A,E,Short}
185  @doc "A type representing a buy order that opens or adds to a 'short' position in a specific asset"
186  const ShortBuyOrder{A,E} = Order{<:OrderType{Buy},A,E,Short}
187  @doc "A type representing a sell order that opens or adds to a 'short' position in a specific asset"
188  const ShortSellOrder{A,E} = Order{<:OrderType{Sell},A,E,Short}
189  @doc "A type representing any immediate order, regardless of the specific asset, exchange, or position side"
190  const AnyImmediateOrder{A,E,P} = Order{<:ImmediateOrderType,A,E,P}
191  
192  @doc "An order that increases the size of a position."
193  const IncreaseOrder{A,E} = Union{BuyOrder{A,E},ShortSellOrder{A,E}}
194  @doc "An order that decreases the size of a position."
195  const ReduceOrder{A,E} = Union{SellOrder{A,E},ShortBuyOrder{A,E}}
196  @doc "A Market Order type that liquidates a position."
197  const LiquidationOrder{S,P,A<:AbstractAsset,E<:ExchangeID} = Order{LiquidationType{S},A,E,P}
198  @doc "A Market Order type called when manually closing a position (to sell the holdings)."
199  const LongReduceOnlyOrder{A<:AbstractAsset,E<:ExchangeID} = Order{
200      ForcedOrderType{Sell},A,E,Long
201  }
202  const ShortReduceOnlyOrder{A<:AbstractAsset,E<:ExchangeID} = Order{
203      ForcedOrderType{Buy},A,E,Short
204  }
205  const ReduceOnlyOrder = Union{LongReduceOnlyOrder,ShortReduceOnlyOrder}
206  
207  @doc """ Defines various order types in the trading system
208  
209  $(TYPEDSIGNATURES)
210  
211  This macro is used to define various order types in the trading system. It takes a boolean value to determine if the order type is a super type, and a list of types to be defined.
212  
213  """
214  macro deforders(issuper, types...)
215      @assert issuper isa Bool
216      out = quote end
217      for t in types
218          type_str = string(t)
219          order_type_str = type_str * "Order"
220          type_sym = Symbol(order_type_str)
221          short_type_sym = Symbol("Short" * order_type_str)
222          # HACK: const/types definitions inside macros can't be revised
223          isdefined(@__MODULE__, type_sym) && continue
224          type = esc(type_sym)
225          short_type = esc(short_type_sym)
226          ordertype = esc(Symbol(type_str * "OrderType"))
227          _orderexpr(pos_side) =
228              if issuper
229                  :(Order{<:$ordertype{S},A,E,$pos_side})
230              else
231                  :(Order{$ordertype{S},A,E,$pos_side})
232              end
233          long_orderexpr = _orderexpr(Long)
234          short_orderexpr = _orderexpr(Short)
235          push!(
236              out.args,
237              quote
238                  const $type{S<:OrderSide,A<:AbstractAsset,E<:ExchangeID} = $long_orderexpr
239                  const $short_type{S<:OrderSide,A<:AbstractAsset,E<:ExchangeID} =
240                      $short_orderexpr
241                  export $type, $short_type
242              end,
243          )
244      end
245      out
246  end
247  @deforders false GTC PostOnly FOK IOC Market
248  @deforders true Limit
249  
250  ==(v1::Type{<:OrderSide}, v2::Type{BuyOrSell}) = true
251  ==(v1::Type{BuyOrSell}, v2::Type{<:OrderSide}) = true
252  @doc """Get the `OrderType` of an order """
253  ordertype(::Order{T}) where {T<:OrderType} = T
254  ordertype(::Type{<:Order{T}}) where {T<:OrderType} = T
255  @doc """Get the `PositionSide` of an order """
256  function positionside(
257      ::Union{Type{O},O}
258  ) where {O<:Order{T,<:AbstractAsset,<:ExchangeID,P}} where {T,P}
259      P
260  end
261  positionside(::Union{P,Type{P}}) where {P<:PositionSide} = P
262  @doc """Get the price and time of an order """
263  pricetime(o::Order) = (price=o.price, time=o.date)
264  @doc """Get the `ExchangeID` of an order """
265  exchangeid(::Order{<:OrderType,<:AbstractAsset,E}) where {E<:ExchangeID} = E
266  commit!(args...; kwargs...) = error("not implemented")
267  @doc """Get the opposite side of an order """
268  opposite(::Type{Buy}) = Sell
269  opposite(::Type{Sell}) = Buy
270  function opposite(::Type{T}) where {S,T<:OrderType{S}}
271      getfield(T.name.module, T.name.name){opposite(S)}
272  end
273  @doc """Get the liquidation side of an order """
274  liqside(::Union{Long,Type{Long}}) = Sell
275  liqside(::Union{Short,Type{Short}}) = Buy
276  @doc """Is the order a liquidation order"""
277  isliquidation(::Order{O}) where {O<:OrderType} = O == LiquidationType
278  sidetopos(::Order{<:OrderType{Buy}}) = Long
279  sidetopos(::Order{<:OrderType{Sell}}) = Short
280  @doc """Test if an order is a long order"""
281  islong(p::Union{<:T,<:Type{<:T}}) where {T<:PositionSide} = p == Long()
282  @doc """Test if an order is a short order"""
283  isshort(p::Union{<:T,<:Type{<:T}}) where {T<:PositionSide} = p == Short()
284  islong(o::Union{<:LongOrder,<:Type{<:LongOrder}}) = true
285  islong(o::Union{<:ShortOrder,<:Type{<:ShortOrder}}) = false
286  isshort(o::Union{<:LongOrder,<:Type{<:LongOrder}}) = false
287  isshort(o::Union{<:ShortOrder,<:Type{<:ShortOrder}}) = true
288  islong(::Nothing) = false
289  isshort(::Nothing) = false
290  @doc """Test if an order is an immediate order"""
291  isimmediate(::Order{<:Union{ImmediateOrderType,MarketOrderType}}) = true
292  isimmediate(::Order) = false
293  @doc """Test if the order position side matches the given position side"""
294  ispos(pos::PositionSide, o::Order) = positionside(o)() == pos
295  order!(args...; kwargs...) = error("not implemented")
296  @doc """Get the trades history of an order """
297  trades(args...; kwargs...) = error("not implemented")
298  
299  include("trades.jl")
300  include("positions.jl")
301  include("balance.jl")
302  include("ohlcv.jl")
303  include("errors.jl")
304  include("print.jl")
305  
306  @doc "Dispatch by `OrderSide` or by an `Order` or `Trade` with the same side as parameter."
307  const BySide{S<:OrderSide} = Union{
308      S,Type{S},Order{<:OrderType{S}},Type{<:Order{<:OrderType{S}}},Trade{<:OrderType{S}}
309  }
310  @doc "A type representing any order with a specific position side"
311  const AnyOrderPos{P<:PositionSide} =
312      Union{O,Type{O}} where {O<:Order{<:OrderType,<:AbstractAsset,<:ExchangeID,P}}
313  @doc "A type representing any trade with a specific position side"
314  const AnyTradePos{P<:PositionSide} =
315      Union{T,Type{T}} where {T<:Trade{<:OrderType,<:AbstractAsset,<:ExchangeID,P}}
316  @doc "Dispatch by `PositionSide` or by an `Order` or `Trade` with the same position side as parameter."
317  const ByPos{P<:PositionSide} = Union{P,Type{P},AnyOrderPos{P},AnyTradePos{P}}
318  
319  # NOTE: Implementing this function for `ByPos` breaks backtesting, don't do it!
320  orderside(::BySide{S}) where {S<:OrderSide} = S
321  isside(what::ByPos{Long}, side::ByPos{Long}) = true
322  isside(what::ByPos{Short}, side::ByPos{Short}) = true
323  @doc "Test if the order side matches the given side"
324  isside(args...) = false
325  @doc """`Buy` as `Long` and `Sell` as `Short`"""
326  sidetopos(::BySide{Buy}) = Long()
327  sidetopos(::BySide{Sell}) = Short()
328  postoside(::ByPos{Long}) = Buy
329  postoside(::ByPos{Short}) = Sell
330  
331  ReduceOnlyOrder(::ByPos{Long}) = LongReduceOnlyOrder
332  ReduceOnlyOrder(::ByPos{Long}, A) = LongReduceOnlyOrder{A}
333  ReduceOnlyOrder(::ByPos{Long}, A, E) = LongReduceOnlyOrder{A,E}
334  ReduceOnlyOrder(::ByPos{Short}) = ShortReduceOnlyOrder
335  ReduceOnlyOrder(::ByPos{Short}, A) = ShortReduceOnlyOrder{A}
336  ReduceOnlyOrder(::ByPos{Short}, A, E) = ShortReduceOnlyOrder{A,E}
337  
338  export Order, OrderType, OrderSide, BySide, Buy, Sell, BuyOrSell, Trade, ByPos
339  export BuyOrder, SellOrder, BuyTrade, SellTrade, AnyBuyOrder, AnySellOrder
340  export ShortBuyTrade, ShortSellTrade
341  export LongOrder, ShortOrder, ShortBuyOrder, ShortSellOrder
342  export IncreaseOrder, ReduceOrder, IncreaseTrade, ReduceTrade, AnyImmediateOrder
343  export LiquidationOrder, ReduceOnlyOrder
344  export OrderError, NotEnoughCash, NotFilled, NotMatched, OrderTimeOut
345  export OrderFailed, OrderCanceled, LiquidationOverride
346  export orderside, positionside, pricetime, islong, isshort, ispos, isimmediate, isside
347  export liqside, sidetopos, opposite
348  export event!,
349      ExchangeEvent,
350      AssetEvent,
351      StrategyEvent,
352      PositionEvent,
353      PositionUpdated,
354      MarginUpdated,
355      LeverageUpdated,
356      BalanceUpdated,
357      OHLCVUpdated
358  export fees