Code Monkey home page Code Monkey logo

strategems.jl's Introduction

Build Status Coverage Status codecov.io

Strategems

Strategems is a Julia package aimed at simplifying and streamlining the process of developing, testing, and optimizing algorithmic/systematic trading strategies. This package is inspired in large part by the quantstrat1,2 package in R, adopting a similar general structure to the building blocks that make up a strategy.

Given the highly iterative nature of event-driven trading strategy development, Julia's high-performance design (particularly in the context of loops) and straightforward syntax would seem to make it a natural fit as a language for systematic strategy research and development. While this package remains early in development, with time the hope is to be able to rapidly implement a trading idea, construct a historical backtest, analyze its results, optimize over a given parameter set, and visualize all of this with great detail.

Dependencies

This package makes heavy use of the Temporal package's TS time series type to facilitate the underlying computations involved in cleaning & preprocessing the data used when testing a Strategy. Additionally, the Indicators package offers many technical analysis functions that have been written/designed with the goal of a highly generalized systematic trading strategy research engine in mind, and should thus should simplify the process of working with this data quite a bit.

Install

The Strategems package can be installed using the standard Julia package manager functions.

# Option A:
Pkg.add("Strategems")

# Option B:
Pkg.clone("https://github.com/dysonance/Strategems.jl")

Anatomy of a Strategy

Below are the basic building blocks making up the general anatomy of a Strategy with respect to the Strategems.jl package design and the type definitions used to facilitate the research workflow.

  • Universe: encapsulation of the assets/securities the strategy is to be allowed to trade
  • Indicator: calculation done on each asset in the universe whose results we think have predictive potential for future price movement
  • ParameterSet: inputs/arguments to the indicator calculations
  • Signal: boolean flag sending messages to the trading logic/rules to be interpreted and acted upon
  • Rule: applications of trading logic derived from interpretations of prior calculations & signals at each time step
  • Strategy: overarching object encapsulating and directing all of the above logic and data to power the backtesting engine

Example Usage

Below is a quick example demonstrating a simple use-case that one might use to get acquainted with how the package works. Note that the custom infix operators denoted by the uparrow and downarrow below are defined in this package as another way of expressing that one variable crosses over another. The intention of this infix operator definition is to hopefully make the definition of a strategy more syntactically expressive and intuitive.

The key indicator used in this strategy is John Ehlers's MESA Adaptive Moving Average (or MAMA for short). This functionality is implemented in the Indicators.jl package described above, and outputs a Matrix (or TS object if one is passed as an input) of two columns, the first being the MAMA itself and the second being the FAMA, or following adaptive moving average.

This strategy simply goes long when the MAMA crosses over the FAMA, and goes short when the FAMA crosses over the MAMA. Below is an implementation that shows how to set default arguments to the Indicators.mama function and run a simple backtest using those parameters, and also define specified ranges over which we might like to see how the strategy behaves under different parameter sets.

using Strategems, Indicators, Temporal, Dates

# define universe and gather data
assets = ["CHRIS/CME_CL1", "CHRIS/CME_RB1"]
universe = Universe(assets)
function datasource(asset::String; save_downloads::Bool=true)::TS
    savedata_path = joinpath(dirname(pathof(Strategems)), "..", "data", "$asset.csv")
    if isfile(savedata_path)
        return Temporal.tsread(savedata_path)
    else
        X = quandl(asset)
        if save_downloads
            if !isdir(dirname(savedata_path))
                mkdir(dirname(savedata_path))
            end
            Temporal.tswrite(X, savedata_path)
        end
        return X
    end
end
gather!(universe, source=datasource)

# define indicators and parameter space
arg_names     = [:fastlimit, :slowlimit]
arg_defaults  = [0.5, 0.05]
arg_ranges    = [0.01:0.01:0.99, 0.01:0.01:0.99]
paramset      = ParameterSet(arg_names, arg_defaults, arg_ranges)
f(x; args...) = Indicators.mama(x; args...)
indicator     = Indicator(f, paramset)

# define signals that will trigger trading decisions
# note the uparrow infix operator is defined to simplify one variable crossing over another
# (similarly for the downarrow infix operator for crossing under)
siglong  = @signal MAMA  FAMA
sigshort = @signal MAMA  FAMA
sigexit  = @signal MAMA == FAMA

# define the trading rules
longrule  = @rule siglong  long 100
shortrule = @rule sigshort  short 100
exitrule  = @rule sigexit  liquidate 1.0
rules     = (longrule, shortrule, exitrule)

# run strategy
strat = Strategy(universe, indicator, rules)
backtest!(strat)
optimize!(strat, samples=0)  # randomly sample the parameter space (0 -> use all combinations)

# cumulative pnl for each combination of the parameter space
strat.backtest.optimization

# visualizing results with the Plots.jl package
using Plots
gr()
(x, y, z) = (strat.backtest.optimization[:,i] for i in 1:3)
surface(x, y, z)

alt text

Roadmap / Wish List

  • Get a sufficiently full-featured type system established to facilitate easy construction of simple strategies
  • Allow more intelligent logic for trading rules
    • Adjust order sizing based on portfolio/account at time t
    • Portfolio optimization logic
    • Risk limits
    • Stop loss rules
  • Define a more diverse set of order types
    • Limit orders
    • Stop orders

strategems.jl's People

Contributors

dysonance avatar femtotrader avatar juliatagbot avatar tkyn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

strategems.jl's Issues

ParameterSet, parameters as named tuples and constraints

Hello,

ParameterSet currently doesn't support constraints among parameters.

Here is a short implementation

import Base: show

const TABWIDTH = 4
const TAB = ' ' ^ TABWIDTH

mutable struct ParameterSet
    arg_names::Vector{Symbol}
    arg_defaults::Vector
    arg_ranges::Vector
    arg_types::Vector{<:Type}
    n_args::Int
    constraints::Vector
    #TODO: refactor out the arg_ prefix (its redundant if they all start with it)
    function ParameterSet(arg_names::Vector{Symbol},
                          arg_defaults::Vector,
                          arg_ranges::Vector=[x:x for x in arg_defaults];
                          constraints::Vector = [p -> true])
        @assert length(arg_names) == length(arg_defaults) == length(arg_ranges)
        @assert eltype.(arg_defaults) == eltype.(arg_ranges)
        arg_types::Vector{<:Type} = eltype.(arg_defaults)
        return new(arg_names, arg_defaults, arg_ranges, arg_types, length(arg_names), constraints)
    end
end

function generate_nt_combinations(ps::ParameterSet)
    itr = Iterators.product(ps.arg_ranges...)
    itr = Iterators.map(t -> (; zip(ps.arg_names, t)...), itr)
    for constraint in ps.constraints
        itr = Iterators.filter(constraint, itr)
    end
    collect(itr)
end

Usage:

arg_names     = [:fastlimit, :slowlimit, :x]
arg_defaults  = [0.5, 0.05, 0.01]
arg_ranges    = [0.01:0.01:1.00, 0.01:0.01:1.00, 0.01:0.01:1.00]
constraints   = [p -> p.fastlimit < p.slowlimit]
paramset      = ParameterSet(arg_names, arg_defaults, arg_ranges, constraints=constraints)
params = generate_nt_combinations(paramset)
params

returns

495000-element Vector{NamedTuple{(:fastlimit, :slowlimit, :x), Tuple{Float64, Float64, Float64}}}:
 (fastlimit = 0.01, slowlimit = 0.02, x = 0.01)
 (fastlimit = 0.01, slowlimit = 0.03, x = 0.01)
 (fastlimit = 0.02, slowlimit = 0.03, x = 0.01)
 (fastlimit = 0.01, slowlimit = 0.04, x = 0.01)
 (fastlimit = 0.02, slowlimit = 0.04, x = 0.01)
 ...
 (fastlimit = 0.96, slowlimit = 1.0, x = 1.0)
 (fastlimit = 0.97, slowlimit = 1.0, x = 1.0)
 (fastlimit = 0.98, slowlimit = 1.0, x = 1.0)
 (fastlimit = 0.99, slowlimit = 1.0, x = 1.0)

which can easily be transformed as DataFrame:

using DataFrame
DataFrame(params)

which returns

495000×3 DataFrame
    Row │ fastlimit  slowlimit  x
        │ Float64    Float64    Float64
────────┼───────────────────────────────
      1 │      0.01       0.02     0.01
      2 │      0.01       0.03     0.01
      3 │      0.02       0.03     0.01
      4 │      0.01       0.04     0.01
      5 │      0.02       0.04     0.01
      6 │      0.03       0.04     0.01
      7 │      0.01       0.05     0.01
      8 │      0.02       0.05     0.01
      9 │      0.03       0.05     0.01
     10 │      0.04       0.05     0.01
     11 │      0.01       0.06     0.01
   ⋮    │     ⋮          ⋮         ⋮
 494991 │      0.9        1.0      1.0
 494992 │      0.91       1.0      1.0
 494993 │      0.92       1.0      1.0
 494994 │      0.93       1.0      1.0
 494995 │      0.94       1.0      1.0
 494996 │      0.95       1.0      1.0
 494997 │      0.96       1.0      1.0
 494998 │      0.97       1.0      1.0
 494999 │      0.98       1.0      1.0
 495000 │      0.99       1.0      1.0
                     494979 rows omitted

Maybe it can help.

Kind regards

PS: see also #8

LoadError: UndefVarError: @signal not defined while loading /Users/balupton/Projects/trading/JuliaTrading.jl/strat.jl, in expression starting on line 30

Hi there,

So I'm using the IDE from Julia Pro 0.6.1.1.

I've created a new directory called JuliaTrading.jl. I've placed the mama.jl file inside of it, but called it strat.jl.

I use the IDE's console to run Pkg.add("Strategems") which works successfully.

Then I use the IDE's "Julia -> Run File" feature, which returns:

LoadError: UndefVarError: @signal not defined
while loading /Users/balupton/Projects/trading/JuliaTrading.jl/strat.jl, in expression starting on line 30
include_string(::String, ::String) at loading.jl:522
include_string(::Module, ::String, ::String) at Compat.jl:464
(::Atom.##57#60{String,String})() at eval.jl:74
withpath(::Atom.##57#60{String,String}, ::String) at utils.jl:30
withpath(::Function, ::String) at eval.jl:38
macro expansion at eval.jl:72 [inlined]
(::Atom.##56#59{Dict{String,Any}})() at task.jl:80

It also happens when installing the package via Pkg.clone("https://github.com/dysonance/Strategems.jl")

strat.jl is:

using Strategems, Temporal, Indicators, Base.Dates
using Base.Test

# define universe and gather data
assets = ["CHRIS/CME_CL1", "CHRIS/CME_RB1"]
universe = Universe(assets)
function datasource(asset::String; save_downloads::Bool=true)::TS
    savedata_path = Pkg.dir("Strategems", "data/$asset.csv")
    if isfile(savedata_path)
        return Temporal.tsread(savedata_path)
    else
        X = quandl(asset)
        if save_downloads
            Temporal.tswrite(X, savedata_path)
        end
        return X
    end
end
gather!(universe, source=datasource)

# define indicators and parameter space
arg_names = [:fastlimit, :slowlimit]
arg_defaults = [0.5, 0.05]
arg_ranges = [0.01:0.01:0.99, 0.01:0.01:0.99]
paramset = ParameterSet(arg_names, arg_defaults, arg_ranges)
f(x; args...) = Indicators.mama(x; args...)
indicator = Indicator(f, paramset)

# define signals that will trigger trading decisions
siglong = @signal MAMA  FAMA
sigshort = @signal MAMA  FAMA
sigexit = @signal MAMA .== FAMA

# define the trading rules
longrule = @rule siglong  long 100
shortrule = @rule sigshort  short 100
exitrule = @rule sigexit  liquidate 1.0
rules = (longrule, shortrule, exitrule)

# run strategy
strat = Strategy(universe, indicator, rules)
backtest!(strat)
optimize!(strat, samples=10)

Example from README is not working - v2

I'm getting the following error after running the example from the README with a clean install of Julia 1.8. I've installed the package using the github URL directly since I've seen some suggestions to do that rather than just adding the package using pkg> add Strategems. Any assistance would be greatly appreciated! Looks like a fantastic package.

Executing task: C:\Program Files\Julia-1.8.3\bin\julia.exe --color=yes --project=C:\Users\mike_.julia\environments\v1.8 c:\data\src\juliaTrader\main.jl

WARNING: both IOExtras and Base export "closewrite"; uses of it in module StreamRequest must be qualified
WARNING: both IOExtras and Base export "closewrite"; uses of it in module ConnectionPool must be qualified
ERROR: LoadError: UndefVarError: closewrite not defined
Stacktrace:
[1] request(::Type{HTTP.StreamRequest.StreamLayer{Union{}}}, io::HTTP.ConnectionPool.Transaction{MbedTLS.SSLContext}, req::HTTP.Messages.Request, body::Vector{UInt8}; reached_redirect_limit::Bool, response_stream::Nothing, iofunction::Nothing, verbose::Int64, kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ HTTP.StreamRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\StreamRequest.jl:83
[2] request(::Type{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}, url::HTTP.URIs.URI, req::HTTP.Messages.Request, body::Vector{UInt8}; proxy::Nothing, socket_type::Type, reuse_limit::Int64, kw::Base.Pairs{Symbol, Union{Nothing, Bool}, Tuple{Symbol, Symbol}, NamedTuple{(:iofunction, :reached_redirect_limit), Tuple{Nothing, Bool}}})
@ HTTP.ConnectionRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\ConnectionRequest.jl:96
[3] request(::Type{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}, ::HTTP.URIs.URI, ::Vararg{Any}; kw::Base.Pairs{Symbol, Union{Nothing, Bool}, Tuple{Symbol, Symbol}, NamedTuple{(:iofunction, :reached_redirect_limit), Tuple{Nothing, Bool}}})
@ HTTP.ExceptionRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\ExceptionRequest.jl:19
[4] (::Base.var"#88#90"{Base.var"#88#89#91"{ExponentialBackOff, HTTP.RetryRequest.var"#2#3"{Bool, HTTP.Messages.Request}, typeof(HTTP.request)}})(::Type, ::Vararg{Any}; kwargs::Base.Pairs{Symbol, Union{Nothing, Bool}, Tuple{Symbol, Symbol}, NamedTuple{(:iofunction, :reached_redirect_limit), Tuple{Nothing, Bool}}})
@ Base .\error.jl:296
[5] #request#1
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\RetryRequest.jl:44 [inlined]
[6] request(::Type{HTTP.MessageRequest.MessageLayer{HTTP.RetryRequest.RetryLayer{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}}}, method::String, url::HTTP.URIs.URI, headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8}; http_version::VersionNumber, target::String, parent::Nothing, iofunction::Nothing, kw::Base.Pairs{Symbol, Bool, Tuple{Symbol}, NamedTuple{(:reached_redirect_limit,), Tuple{Bool}}})
@ HTTP.MessageRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\MessageRequest.jl:51
[7] request(::Type{HTTP.BasicAuthRequest.BasicAuthLayer{HTTP.MessageRequest.MessageLayer{HTTP.RetryRequest.RetryLayer{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}}}}, method::String, url::HTTP.URIs.URI, headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8}; kw::Base.Pairs{Symbol, Bool, Tuple{Symbol}, NamedTuple{(:reached_redirect_limit,), Tuple{Bool}}})
@ HTTP.BasicAuthRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\BasicAuthRequest.jl:28
[8] request(::Type{HTTP.RedirectRequest.RedirectLayer{HTTP.BasicAuthRequest.BasicAuthLayer{HTTP.MessageRequest.MessageLayer{HTTP.RetryRequest.RetryLayer{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}}}}}, method::String, url::HTTP.URIs.URI, headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8}; redirect_limit::Int64, forwardheaders::Bool, kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ HTTP.RedirectRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\RedirectRequest.jl:24
[9] request(::Type{HTTP.RedirectRequest.RedirectLayer{HTTP.BasicAuthRequest.BasicAuthLayer{HTTP.MessageRequest.MessageLayer{HTTP.RetryRequest.RetryLayer{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}}}}}, method::String, url::HTTP.URIs.URI, headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8})
@ HTTP.RedirectRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\RedirectRequest.jl:18
[10] request(method::String, url::String, h::Vector{Pair{SubString{String}, SubString{String}}}, b::Vector{UInt8}; headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8}, query::Nothing, kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ HTTP C:\Users\mike_.julia\packages\HTTP\IAI92\src\HTTP.jl:314
[11] request (repeats 2 times)
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\HTTP.jl:312 [inlined]
[12] #get#12
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\HTTP.jl:391 [inlined]
[13] get
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\HTTP.jl:391 [inlined]
[14] quandl(code::String; from::String, thru::String, freq::Char, calc::String, sort::Char, rows::Int64, auth::String)
@ Temporal C:\Users\mike_.julia\packages\Temporal\aaVHO\src\data\quandl.jl:77
[15] quandl
@ C:\Users\mike_.julia\packages\Temporal\aaVHO\src\data\quandl.jl:50 [inlined]
[16] datasource(asset::String; save_downloads::Bool)
@ Main c:\data\src\juliaTrader\main.jl:11
[17] datasource
@ c:\data\src\juliaTrader\main.jl:6 [inlined]
[18] gather!(universe::Universe; source::typeof(datasource), verbose::Bool)
@ Strategems C:\Users\mike_.julia\packages\Strategems\9FJXr\src\model\universe.jl:41
[19] top-level scope
@ c:\data\src\juliaTrader\main.jl:22
in expression starting at c:\data\src\juliaTrader\main.jl:22

caused by: EOFError: read end of file
Stacktrace:
[1] read_to_buffer(t::HTTP.ConnectionPool.Transaction{MbedTLS.SSLContext}, sizehint::Int64)
@ HTTP.ConnectionPool C:\Users\mike_.julia\packages\HTTP\IAI92\src\ConnectionPool.jl:252
[2] readuntil(t::HTTP.ConnectionPool.Transaction{MbedTLS.SSLContext}, f::Function, sizehint::Int64)
@ HTTP.ConnectionPool C:\Users\mike_.julia\packages\HTTP\IAI92\src\ConnectionPool.jl:271
[3] readuntil
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\ConnectionPool.jl:269 [inlined]
[4] readheaders
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\Messages.jl:471 [inlined]
[5] startread(http::HTTP.Streams.Stream{HTTP.Messages.Response, HTTP.ConnectionPool.Transaction{MbedTLS.SSLContext}})
@ HTTP.Streams C:\Users\mike_.julia\packages\HTTP\IAI92\src\Streams.jl:155
[6] macro expansion
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\StreamRequest.jl:67 [inlined]
[7] macro expansion
@ .\task.jl:454 [inlined]
[8] request(::Type{HTTP.StreamRequest.StreamLayer{Union{}}}, io::HTTP.ConnectionPool.Transaction{MbedTLS.SSLContext}, req::HTTP.Messages.Request, body::Vector{UInt8}; reached_redirect_limit::Bool, response_stream::Nothing, iofunction::Nothing, verbose::Int64, kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ HTTP.StreamRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\StreamRequest.jl:57
[9] request(::Type{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}, url::HTTP.URIs.URI, req::HTTP.Messages.Request, body::Vector{UInt8}; proxy::Nothing, socket_type::Type, reuse_limit::Int64, kw::Base.Pairs{Symbol, Union{Nothing, Bool}, Tuple{Symbol, Symbol}, NamedTuple{(:iofunction, :reached_redirect_limit), Tuple{Nothing, Bool}}})
@ HTTP.ConnectionRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\ConnectionRequest.jl:96
[10] request(::Type{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}, ::HTTP.URIs.URI, ::Vararg{Any}; kw::Base.Pairs{Symbol, Union{Nothing, Bool}, Tuple{Symbol, Symbol}, NamedTuple{(:iofunction, :reached_redirect_limit), Tuple{Nothing, Bool}}})
@ HTTP.ExceptionRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\ExceptionRequest.jl:19
[11] (::Base.var"#88#90"{Base.var"#88#89#91"{ExponentialBackOff, HTTP.RetryRequest.var"#2#3"{Bool, HTTP.Messages.Request}, typeof(HTTP.request)}})(::Type, ::Vararg{Any}; kwargs::Base.Pairs{Symbol, Union{Nothing, Bool}, Tuple{Symbol, Symbol}, NamedTuple{(:iofunction, :reached_redirect_limit), Tuple{Nothing, Bool}}})
@ Base .\error.jl:296
[12] #request#1
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\RetryRequest.jl:44 [inlined]
[13] request(::Type{HTTP.MessageRequest.MessageLayer{HTTP.RetryRequest.RetryLayer{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}}}, method::String, url::HTTP.URIs.URI, headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8}; http_version::VersionNumber, target::String, parent::Nothing, iofunction::Nothing, kw::Base.Pairs{Symbol, Bool, Tuple{Symbol}, NamedTuple{(:reached_redirect_limit,), Tuple{Bool}}})
@ HTTP.MessageRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\MessageRequest.jl:51
[14] request(::Type{HTTP.BasicAuthRequest.BasicAuthLayer{HTTP.MessageRequest.MessageLayer{HTTP.RetryRequest.RetryLayer{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}}}}, method::String, url::HTTP.URIs.URI, headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8}; kw::Base.Pairs{Symbol, Bool, Tuple{Symbol}, NamedTuple{(:reached_redirect_limit,), Tuple{Bool}}})
@ HTTP.BasicAuthRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\BasicAuthRequest.jl:28
[15] request(::Type{HTTP.RedirectRequest.RedirectLayer{HTTP.BasicAuthRequest.BasicAuthLayer{HTTP.MessageRequest.MessageLayer{HTTP.RetryRequest.RetryLayer{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}}}}}, method::String, url::HTTP.URIs.URI, headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8}; redirect_limit::Int64, forwardheaders::Bool, kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ HTTP.RedirectRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\RedirectRequest.jl:24
[16] request(::Type{HTTP.RedirectRequest.RedirectLayer{HTTP.BasicAuthRequest.BasicAuthLayer{HTTP.MessageRequest.MessageLayer{HTTP.RetryRequest.RetryLayer{HTTP.ExceptionRequest.ExceptionLayer{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}}}}}}, method::String, url::HTTP.URIs.URI, headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8})
@ HTTP.RedirectRequest C:\Users\mike_.julia\packages\HTTP\IAI92\src\RedirectRequest.jl:18
[17] request(method::String, url::String, h::Vector{Pair{SubString{String}, SubString{String}}}, b::Vector{UInt8}; headers::Vector{Pair{SubString{String}, SubString{String}}}, body::Vector{UInt8}, query::Nothing, kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
@ HTTP C:\Users\mike_.julia\packages\HTTP\IAI92\src\HTTP.jl:314
[18] request (repeats 2 times)
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\HTTP.jl:312 [inlined]
[19] #get#12
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\HTTP.jl:391 [inlined]
[20] get
@ C:\Users\mike_.julia\packages\HTTP\IAI92\src\HTTP.jl:391 [inlined]
[21] quandl(code::String; from::String, thru::String, freq::Char, calc::String, sort::Char, rows::Int64, auth::String)
@ Temporal C:\Users\mike_.julia\packages\Temporal\aaVHO\src\data\quandl.jl:77
[22] quandl
@ C:\Users\mike_.julia\packages\Temporal\aaVHO\src\data\quandl.jl:50 [inlined]
[23] datasource(asset::String; save_downloads::Bool)
@ Main c:\data\src\juliaTrader\main.jl:11
[24] datasource
@ c:\data\src\juliaTrader\main.jl:6 [inlined]
[25] gather!(universe::Universe; source::typeof(datasource), verbose::Bool)
@ Strategems C:\Users\mike_.julia\packages\Strategems\9FJXr\src\model\universe.jl:41
[26] top-level scope
@ c:\data\src\juliaTrader\main.jl:22

get_run_params is broken

julia> using Strategems

julia> arg_names     = [:fastlimit, :slowlimit]
2-element Array{Symbol,1}:
 :fastlimit
 :slowlimit

julia> arg_defaults  = [0.5, 0.05]
2-element Array{Float64,1}:
 0.5 
 0.05

julia> arg_ranges    = [0.01:0.01:0.99, 0.01:0.01:0.99]
2-element Array{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},1}:
 0.01:0.01:0.99
 0.01:0.01:0.99

julia> paramset      = ParameterSet(arg_names, arg_defaults, arg_ranges)
Parameters:
    (1) fastlimit    0.5    {0.01:0.01:0.99} :: Float64
    (2) slowlimit    0.05    {0.01:0.01:0.99} :: Float64

julia> get_run_params(paramset)
ERROR: function get_param_combos does not accept keyword arguments
Stacktrace:
 [1] kwfunc(::Any) at ./boot.jl:237
 [2] #get_run_params#4(::Int64, ::Function, ::Strategems.ParameterSet) at /home/femto/.julia/v0.6/Strategems/src/paramset.jl:50
 [3] get_run_params(::Strategems.ParameterSet) at /home/femto/.julia/v0.6/Strategems/src/paramset.jl:49

Parameter Set Constraints

When defining a ParameterSet object with ranges corresponding to each of its variables (i.e. the arg_ranges member), currently there isn't any way to constrain the parameter space using the relationships between the variables.

There should be logic to support these kinds of inter-variable relationship constraints. For example, take a simple strategy using two moving averages, one with a short lookback period and one with a long lookback period. Suppose our rule is to go long when the short MA crosses over the long MA, and we want to simulate this strategy over a bunch of pairs of lookback windows. There should be logic to constrain the simulation such that the short MA's lookback must be less than the long MA's lookback.

More organized test cases

Hello,

Just a comment about tests not really an issue... you should probably use @testset

Kind regards

Parameter exploration - nicer API

Hello,

Python have an interesting library with a nice API.
https://pypet.readthedocs.io/
Maybe you should consider something like this.

This Julia parameter exploration toolkit could be even be a standalone project (with few dependencies).

Similar discussion https://www.quantopian.com/posts/parameter-exploration-slash-running-several-backtests-from-notebook

These discussions about generators and Julia might be interesting for that purpose https://discourse.julialang.org/t/pygen-python-style-generators/3451
https://discourse.julialang.org/t/ann-resumablefunctions/5711

Kind regards

Random Sampling of Parameter Space Combinations

Instead of running all possible combinations of all parameters in the space, it probably wouldn't be too bad to implement functionality that randomly samples a user-suppled number n parameter combinations from the entire space. Then you could run an optimization much more quickly if desired while still getting a broader span of the parameter space than doing the combinations sequentially.

Should also support an argument that allows setting the seed of the random number generator.

Bonus advantage: could greatly expedite test cases.

"LoadError: syntax: "..." expression outside call"

I've installed and then reinstalled Julia 0.7.0v from the official site, made a clean git from the latest pull from the author, then, proceeded to run the examples/mama.jl and the test/runtest.jl, both didn't sucessfully compile and gave me the following stack:

**ERROR: LoadError: LoadError: syntax: "..." expression outside call**
Stacktrace:
 [1] include at .\boot.jl:317 [inlined]
 [2] include_relative(::Module, ::String) at .\loading.jl:1038
 [3] include at .\sysimg.jl:29 [inlined]
 [4] include(::String) at C:\Users\MyUser\.julia\packages\Strategems\PJiUR\src\Strategems.jl:3
 [5] top-level scope at none:0
 [6] include at .\boot.jl:317 [inlined]
 [7] include_relative(::Module, ::String) at .\loading.jl:1038
 [8] include(::Module, ::String) at .\sysimg.jl:29
 [9] top-level scope at none:2
 [10] eval at .\boot.jl:319 [inlined]
 [11] eval(::Expr) at .\client.jl:399
 [12] top-level scope at .\none:3
in expression starting at C:\Users\MyUser\.julia\packages\Strategems\PJiUR\src\rule.jl:15
in expression starting at C:\Users\MyUser\.julia\packages\Strategems\PJiUR\src\Strategems.jl:34
ERROR: LoadError: Failed to precompile Strategems [054b7d4e-9922-5ad3-8b46-7dbe695a681f] to C:\Users\laughing man\.julia\compiled\v0.7\Strategems\qq9u1.ji.
Stacktrace:
 [1] error(::String) at .\error.jl:33
 [2] macro expansion at .\logging.jl:313 [inlined]
 [3] compilecache(::Base.PkgId, ::String) at .\loading.jl:1185
 [4] _require(::Base.PkgId) at .\logging.jl:311
 [5] require(::Base.PkgId) at .\loading.jl:852
 [6] macro expansion at .\logging.jl:311 [inlined]
 [7] require(::Module, ::Symbol) at .\loading.jl:834
 [8] include at .\boot.jl:317 [inlined]
 [9] include_relative(::Module, ::String) at .\loading.jl:1038
 [10] include(::Module, ::String) at .\sysimg.jl:29
 [11] exec_options(::Base.JLOptions) at .\client.jl:239
 [12] _start() at .\client.jl:432
in expression starting at C:\User\MyUser\Documents\Strategems.jl\examples\mama.jl:1

Which leads us again to:

macro rule(logic::Expr, args...)
    trigger = :($(logic.args[2]))
    #action = :($(logic.args[3])$((args...)))
    action = :($(logic.args[3]))
    args = :($(args))
    return esc(:(Rule($trigger, $action, $args)))
end

I have a vague idea on how to fix the code, but since it's a default error, I thought I'd report as an issue.

Example from README is not working

I found very interesting this tool, so I started from the example posted in the README but I was not able to make it work. I'm using Julia 1.4.2. Here is the full stack trace, but the error occurs when creating the Universe instance (apparently, some bad access in an empty list):

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.4.2 (2020-05-23)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> using Strategems, Indicators, Temporal, Dates
julia> assets = ["CHRIS/CME_CL1", "CHRIS/CME_RB1"]

2-element Array{String,1}:
 "CHRIS/CME_CL1"
 "CHRIS/CME_RB1"

julia> universe = Universe(assets)
# Universe:
    Asset 1:    CHRIS/CME_CL1

Error showing value of type Universe:
ERROR: BoundsError: attempt to access 0-element Array{Date,1} at index [1]
Stacktrace:
 [1] getindex(::Array{Date,1}, ::Int64) at ./array.jl:788
 [2] show(::IOContext{REPL.Terminals.TTYTerminal}, ::Universe) at /home/leferrad/.julia/packages/Strategems/TJzbq/src/universe.jl:63
 [3] show(::IOContext{REPL.Terminals.TTYTerminal}, ::MIME{Symbol("text/plain")}, ::Universe) at ./multimedia.jl:47
 [4] display(::REPL.REPLDisplay, ::MIME{Symbol("text/plain")}, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:137
 [5] display(::REPL.REPLDisplay, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:141
 [6] display(::Any) at ./multimedia.jl:323
 [7] #invokelatest#1 at ./essentials.jl:712 [inlined]
 [8] invokelatest at ./essentials.jl:711 [inlined]
 [9] print_response(::IO, ::Any, ::Bool, ::Bool, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:161
 [10] print_response(::REPL.AbstractREPL, ::Any, ::Bool, ::Bool) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:146
 [11] (::REPL.var"#do_respond#38"{Bool,REPL.var"#48#57"{REPL.LineEditREPL,REPL.REPLHistoryProvider},REPL.LineEditREPL,REPL.LineEdit.Prompt})(::Any, ::Any, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:729
 [12] #invokelatest#1 at ./essentials.jl:712 [inlined]
 [13] invokelatest at ./essentials.jl:711 [inlined]
 [14] run_interface(::REPL.Terminals.TextTerminal, ::REPL.LineEdit.ModalInterface, ::REPL.LineEdit.MIState) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.4/REPL/src/LineEdit.jl:2354
 [15] run_frontend(::REPL.LineEditREPL, ::REPL.REPLBackendRef) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:1055
 [16] run_repl(::REPL.AbstractREPL, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:206
 [17] (::Base.var"#764#766"{Bool,Bool,Bool,Bool})(::Module) at ./client.jl:383
 [18] #invokelatest#1 at ./essentials.jl:712 [inlined]
 [19] invokelatest at ./essentials.jl:711 [inlined]
 [20] run_main_repl(::Bool, ::Bool, ::Bool, ::Bool, ::Bool) at ./client.jl:367
 [21] exec_options(::Base.JLOptions) at ./client.jl:305
 [22] _start() at ./client.jl:484

Let me know if you need something else to reproduce this error, in order to detect the bug and make a PR to fix it.

Documentation

Need to write more thorough documentation for package functionality.

  • Examples
    • Full walkthrough
    • Universe data gathering usage
  • New infix operators
    • Crossover alias (up arrow)
    • Crossunder alias (down arrow)

Parallelism

Should be able to run multiple backtests in parallel when doing parameter optimization runs.

Incompatible with Julia 1.0v

When installing the package, you're met with the following message:

ERROR: Unsatisfiable requirements detected for package Strategems [054b7d4e]:
 Strategems [054b7d4e] log:
 ├─possible versions are: 0.0.1-0.0.5 or uninstalled
 ├─restricted to versions * by an explicit requirement, leaving only versions 0.0.1-0.0.5
 └─restricted by julia compatibility requirements to versions: uninstalled — no versions left

The whole Git seems to have been deprecated, is the developer still active?

Sample data and reorganisation / chunk of data

Hello,

We should have a sample data project
(it could be in a separate repository)

I think it will be better to have sample data close to something that can be use in backtesting production.

I think we should split data directory from datasource and into bars, ticks, orderbooks

I also think that we should use "round" date range for data (chunk of data)

A possible organization could be

data
    datasource1
        bars
            D
                2010
                    GOOG.csv
                    another_ticker (to define).csv
                2011
                    GOOG.csv
                    another_ticker (to define).csv
                2012
                    GOOG.csv
                    another_ticker (to define).csv
                2013
                    GOOG.csv
                    another_ticker (to define).csv
                2014
                    GOOG.csv
                    another_ticker (to define).csv
                2015
                    GOOG.csv
                    another_ticker (to define).csv
        ticks
            2015
                2015-02
                    2015-02-01
                        MSFT.csv
                        AMZN.csv
                        GOOG.csv

This kind of organization will urge us to work with chunk of data (especially if we want to deal with tick data)

Kind regards

PS : You might have a look at
mhallsmoore/qstrader#106

Order Type Implementations

Implement a Julia type, Order (or AbstractOrder from which more concrete order types could be children, like MarketOrder, LimitOrder, StopOrder, etc.) for storing, processing, and manipulating orders.

This type should be able to interact with a Portfolio object in a highly structured order queue processing pattern. In other words, when a Signal/Rule triggers some market action, it should be communicated through an Order type.

At a broad level I imagine it would look somewhat like this.

  1. Indicator function runs computations on raw market data at each time t
  2. Signal function fires based on results of Indicator calculations
  3. Rule dictates what action to take when the Signal is triggered
  4. At time t, when a Rule is processed, it should should output an Order
  5. This Order object should then be placed in an order queue to be handled at time t+1
  6. Based on the order type logic and perhaps some other settings (e.g. which price to trade at, generally assumed to be the open), at time t+1 all orders should be "processed"
  7. This Order "processing" logic should probably depend on a Portfolio object, which should receive a series of Orders and make the appropriate adjustments at time t+1 to the position holdings and update the per-asset portfolio weights

Questions:

  • Given an order queue containing some n, how should capital constraints be applied in deciding how to allocate trades under capital budgeting constraints? In other words, if we have $100 in the bank and all of today's orders would have us buying $120 worth of stocks, how should these trades be allocated? Just allocate pro-rata based on the sizes of all the orders?
  • How should the above consideration be altered in the face of different asset classes? There should be logic added at some point that is intelligent enough to know that submitting an Order to buy an equity constitutes a cash outflow at time t but that the same does not go for a futures contract. But futures contracts should probably have their own logic as well (e.g. initial margin, maintenance margin, etc).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.