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