/ Strategies / src / module.jl
module.jl
  1  using Collections: AssetCollection, Collections as coll, Instances, Data
  2  
  3  using .Instances: AssetInstance, Position, MarginMode, PositionSide, ishedged, Instances
  4  using .Instances: CurrencyCash, CCash
  5  using .Instances.Exchanges
  6  using .Instances: OrderTypes
  7  using .OrderTypes: Order, OrderType, AnyBuyOrder, AnySellOrder, Buy, Sell, OrderSide
  8  using .OrderTypes: OrderError, StrategyEvent, Instruments
  9  using .Instruments: AbstractAsset, Cash, cash!, Derivatives.Derivative
 10  
 11  import .Data: candleat, openat, highat, lowat, closeat, volumeat, closelast
 12  using .Data: Misc, EventTrace
 13  using .Data.DataFrames: nrow
 14  using .Data.DataStructures: SortedDict, Ordering
 15  import .Data.DataStructures: lt
 16  using .Data: closelast
 17  using .Misc
 18  using .Misc: DFT, IsolatedMargin, TimeTicks, Lang
 19  import .Misc: reset!, Long, Short, attrs, call!, call!
 20  using .TimeTicks
 21  using .Lang: @lget!
 22  using Pkg: Pkg
 23  
 24  @doc "The base type for all strategies."
 25  abstract type AbstractStrategy end
 26  
 27  @doc "`AssetInstance` by `ExchangeID`"
 28  const ExchangeAsset{E} = AssetInstance{T,E} where {T<:AbstractAsset}
 29  @doc "`Order` by `ExchangeID`"
 30  const ExchangeOrder{E} = Order{O,T,E} where {O<:OrderType,T<:AbstractAsset}
 31  @doc "`BuyOrder` by `ExchangeID`"
 32  const ExchangeBuyOrder{E} = AnyBuyOrder{P,T,E} where {P<:PositionSide,T<:AbstractAsset}
 33  @doc "`SellOrder` by `ExchangeID`"
 34  const ExchangeSellOrder{E} = AnySellOrder{P,T,E} where {P<:PositionSide,T<:AbstractAsset}
 35  @doc "`PriceTime` named tuple"
 36  const PriceTime = NamedTuple{(:price, :time),Tuple{DFT,DateTime}}
 37  @doc "Ordering for buy orders (highest price first)"
 38  struct BuyPriceTimeOrdering <: Ordering end
 39  @doc "Ordering for sell orders (lowest price first)"
 40  struct SellPriceTimeOrdering <: Ordering end
 41  function lt(::BuyPriceTimeOrdering, a, b)
 42      a.price > b.price || (a.price == b.price && a.time < b.time)
 43  end
 44  function lt(::SellPriceTimeOrdering, a, b)
 45      a.price < b.price || (a.price == b.price && a.time < b.time)
 46  end
 47  @doc "`SortedDict` of holding buy orders"
 48  const BuyOrdersDict{E} = SortedDict{PriceTime,ExchangeBuyOrder{E},BuyPriceTimeOrdering}
 49  @doc "`SortedDict` of holding sell orders"
 50  const SellOrdersDict{E} = SortedDict{PriceTime,ExchangeSellOrder{E},SellPriceTimeOrdering}
 51  
 52  @doc """The strategy is the core type of the framework.
 53  
 54  $(FIELDS)
 55  
 56  The strategy type is concrete according to:
 57  - Name (Symbol)
 58  - Exchange (ExchangeID), read from config
 59  - Quote cash (Symbol), read from config
 60  - Margin mode (MarginMode), read from config
 61  - Execution mode (ExecMode), read from config
 62  
 63  Conventions for strategy defined attributes:
 64  - `S`: the strategy type.
 65  - `SC`: the strategy type (exchange generic).
 66  - `TF`: the smallest `timeframe` that the strategy uses
 67  - `DESCRIPTION`: Name or short description for the strategy could be different from module name
 68  """
 69  struct Strategy{X<:ExecMode,N,E<:ExchangeID,M<:MarginMode,C} <: AbstractStrategy
 70      "The strategy module"
 71      self::Module
 72      "The `Config` the strategy was instantiated with"
 73      config::Config
 74      "The smallest timeframe the strategy uses"
 75      timeframe::TimeFrame
 76      "The quote currency used for trades"
 77      cash::CCash{E}{C}
 78      "Cash kept busy by pending orders"
 79      cash_committed::CCash{E}{C}
 80      "Active buy orders"
 81      buyorders::Dict{ExchangeAsset{E},BuyOrdersDict{E}}
 82      "Active sell orders"
 83      sellorders::Dict{ExchangeAsset{E},SellOrdersDict{E}}
 84      "Assets with non zero balance"
 85      holdings::Set{ExchangeAsset{E}}
 86      "All the assets that the strategy knows about"
 87      universe::AssetCollection
 88      "A lock for thread safety"
 89      lock::SafeLock
 90      @doc """ Initializes a new `Strategy` object
 91  
 92      $(TYPEDSIGNATURES)
 93  
 94      This function takes a module, execution mode, margin mode, timeframe, exchange, and asset collection to create a new `Strategy` object. 
 95      It also accepts a `config` object to set specific parameters. 
 96      The function validates the universe of assets and the strategy's cash, sets the exchange, and initializes orders and holdings. 
 97  
 98      """
 99      function Strategy(
100          self::Module,
101          mode::ExecMode,
102          margin::MarginMode,
103          timeframe::TimeFrame,
104          exc::Exchange,
105          uni::AssetCollection;
106          config::Config
107      )
108          @assert !ishedged(margin) "Hedged margin not yet supported."
109          ca = CurrencyCash(exc, config.qc, config.initial_cash)
110          if !isempty(uni) && !coll.iscashable(ca, uni)
111              @warn "Assets within the strategy universe don't match the strategy cash! ($(nameof(ca)))"
112          end
113          _no_inv_contracts(exc, uni)
114          ca_comm = CurrencyCash(exc, config.qc, 0.0)
115          eid = typeof(exc.id)
116          if issandbox(exc) && mode isa Paper
117              @warn "Exchange should not be in sandbox mode if strategy is in paper mode."
118          end
119          holdings = Set{ExchangeAsset{eid}}()
120          buyorders = Dict{ExchangeAsset{eid},SortedDict{PriceTime,ExchangeBuyOrder{eid}}}()
121          sellorders = Dict{ExchangeAsset{eid},SortedDict{PriceTime,ExchangeSellOrder{eid}}}()
122          name = nameof(self)
123          # set exchange
124          mm = margin isa IsolatedMargin ? "isolated" : "cross"
125          marginmode!(exc, mm, "")
126          setattr!(config, exc, :exc)
127          new{typeof(mode),name,eid,typeof(margin),config.qc}(
128              self,
129              config,
130              timeframe,
131              ca,
132              ca_comm,
133              buyorders,
134              sellorders,
135              holdings,
136              uni,
137              SafeLock(),
138          )
139      end
140  end
141  
142  # NOTE: it's possible these should be functors to avoid breaking Revise
143  @doc "Simulation strategy."
144  const SimStrategy = Strategy{Sim}
145  @doc "Paper trading strategy."
146  const PaperStrategy = Strategy{Paper}
147  @doc "Live trading strategy."
148  const LiveStrategy = Strategy{Live}
149  @doc "Real time strategy (`Paper`, `Live`)."
150  const RTStrategy = Strategy{<:Union{Paper,Live}}
151  @doc "Isolated margin strategy."
152  const IsolatedStrategy = Strategy{X,N,<:ExchangeID,Isolated,C} where {X<:ExecMode,N,C}
153  @doc "Cross margin strategy."
154  const CrossStrategy = Strategy{X,N,<:ExchangeID,Cross,C} where {X<:ExecMode,N,C}
155  @doc "Strategy with isolated or cross margin."
156  const MarginStrategy =
157      Strategy{X,N,<:ExchangeID,<:Union{Isolated,Cross},C} where {X<:ExecMode,N,C}
158  @doc "Strategy with no margin at all."
159  const NoMarginStrategy = Strategy{X,N,<:ExchangeID,NoMargin,C} where {X<:ExecMode,N,C}
160  @doc "Functions that are called (with the strategy as argument) right after strategy construction."
161  const STRATEGY_LOAD_CALLBACKS = (; (m => Function[] for m in (:sim, :paper, :live))...)
162  
163  include("methods.jl")
164  include("interface.jl")
165  include("load.jl")
166  include("utils.jl")
167  include("print.jl")
168  
169  export Strategy, strategy, strategy!, reset!, default!
170  export @interface, id, assets, exchange, universe, throttle, marketsid, asset_bysym, symsdict
171  export StartStrategy, StopStrategy, LoadStrategy, ResetStrategy, WarmupPeriod, StrategyMarkets
172  export SimStrategy, PaperStrategy, LiveStrategy, RTStrategy, IsolatedStrategy, CrossStrategy
173  export attr, attrs, setattr!
174  export issim, ispaper, islive