Introduction

AimsQuant offers an easy to use platform to research investment ideas. This platform helps in finding patterns in the stock market and testing variety of investment ideas. We provide high quality EOD/Minute resolution data, multitude of technical indicators and computation tools like Backtester to test the historical profitability of an investment idea. The backtester can be used in two modes, UI and Code. Our computation engine is written in Julia which allows for simple programming and super fast computations.

The platform displays the backtest performance as it runs (realtime). On successful completion, it displays broad set of analytics to discover more about profitability and overall performance of the strategy. As of now, you can run free 20 backtests everyday to test and improve your investment strategy.

Data Sources

On the platform, we provide price data for Indian equity/cash markets only. We have daily data starting from year 2007 and minute data starting 2018. The price data is adjusted for corporate actions like splits and dividend distributions. The backtester uses exact data for rebalancing the portfolio and adequately adjusts the portfolio and orders for any splits and dividends. However, adjusted (for corporate actions) data is made available for historical analysis and finding patterns with technical analysis. In the API section, there is a list of functions to fetch price data from the data stores.

We understand that just the price data is not sufficient to create consistently profitable investment strategies so we are working with data vendors to quickly bring to you adequate fundamental data.

Summary
  • Only Equity data for Indian markets from 2007
  • Daily and Minute Frequency Data
  • Corporate actions adjusted data for analysis
  • Actual data (true close prices) to rebalance the portfolio
  • Limit of 20 backtests everyday

What is Backtesting?

Backtesting is a testing process for trading and investment strategies. It estimates the performance of a strategy or model if employed during a past period. It helps in analyzing the profitability along with risk and return characteristics of a strategy over a period of time. It is a widely used industry practice where trading strategies are thoroughly tested before allocation of actual money. This makes it an integral part of developing an automated trading system.

How to use the platform?

The platform is very easy to use. The platform has three major panels. Logic Editor and/or Code Editor, Editor Toolbar and Settings Panel

Logic Editor

The Logic Editor is a UI based strategy creator. A user can create stratgy logic with a few clicks (without writing any code). User must add at-least one entry condition to run a successful backtest.

screenshot

Code Editor

For programming enthusiasts, the platform also provides a Code Editor to write investment strategies. A user can edit/write a Julia script detailing the logic of the trading strategy. The user must create two necessary functions for a successful backtest.

screenshot

Editor Toolbar

The toolbar provides quick functionalities to create, edit and save the strategy. The user can launch by pressing the Play button. The user can also visualize history of all backtests by pressing the Chart icon. Below is a descriptive picture of the editor toolbar

screenshot

Settings Panel

The panel on the right of Code Editor is called Settings Panel. The settings panel helps in easy selection of parameters of the trading strategy. Basic Settings include parameters like Start and End Date of the backtester and; Initial Capital of the strategy. Also, a list of stocks can be selected from the settings panel.

However, the parameters will be overwritten if redefined via the code editor in initialize()

screenshot

Codeless Backtesting

The new Flow editor is very unique as it helps in creating a trading strategy just with a few clicks. User can now add trading conditions by selecting required technical indicators and other parameters via UI. Also, for programming enthusiasts, the strategy code is generated in the background.

ENTRY/EXIT Conditions

An ENTRY/EXIT condition is a logical condition based on market data when a entry/exit trade is triggered. It is generally a combination of two trading/technical indicators. The platform provides upto 40 commonly used technical indicators. User can select upto 5 entry/exit conditions. User must select at-least one entry condition but there is no explicit requirement of exit conditions to run a successful backtest.

screenshot

Universe Selection

User can choose upto 20 stocks simultaneously from settings panel. The stocks can also be filtered using a predefined market index

screenshot

Stop Loss/Profit Target

In addition to exit conditions, user can choose stop loss and profit target from the settings panel. The value must be provided in % units.

API

I. API Entities

1. Security

Security represents a trading instrument on an exchange. In the platform, a security can be uniquely identified by it's symbol which is a combination of string ticker and an integer identifier. Security is a building block of an instrument universe in a trading strategy. In the platform, it is used pervasively used to fetch prices, place orders and learn about positions in the portfolio.

DEFAULT exchange is National Stock Exchange of India (identified by code "NSE")
DEFAULT country is India (identified by code "IN")
DEFAULT security type is Equity (identified by code "EQ")
*Currently, the platform supports only equity ("EQ") type securities.


#Security Symbol: Combination of string ticker and an integer identifier
type SecuritySymbol
    id::Int64
    ticker::String
end
#Security Object
type Security
    symbol::SecuritySymbol
    name::String
    exchange::String
    country::String
    securitytype::String
    startdate::DateTime
    enddate::DateTime
end
                                                
                                        
2. Universe

This represents a collection of securities. It tracks all the securities available in a trading strategy at all times. A security must only be stored and accessed from this collection to make any changes to the trading strategy.

3. Order

It is an instruction sent to a broker to enter, exit or change a position. It helps to modify the quantity of stocks held in an instrument.
To increase the quantity in a Security A from 100 shares to 200 shares, a BUY order is placed for 100 shares. Similarly, in order to reduce the quantity in a Security, a SELL order is placed. A SELL order of 50 shares aims to reduce the quantity by 50 shares.


# Order Object
type Order
    id::UInt64
    securitysymbol::SecuritySymbol
    quantity::Int64 
    remainingquantity::Int64
    price::Float64
    ordertype::OrderType
    datetime::DateTime 
    orderstatus::OrderStatus 
    tag::String
end
                                                
                                            

Every order is identified by a unique integer identifier inside AimsQuant platform. The user can inquire about the order status by inspecting the fields mentioned. In addition to remainingquantity, a quick look into orderstatus tells the current stage of the order cycle.

As order quantities are signed (negative for SELL and positive for BUY), "less" or "greater" should be considered in terms of absolute values only!

List of OrderStatus:
  • Filled: When the order is complete. In this case, the remaining quantity of the order becomes 0
  • PartiallyFilled: When the order is not complete yet but remaining quantity is less than the original quantity
  • Pending: When order is submitted but remaining quantity is same as original quantity
List of OrderType:
  • MarketOnOpen: Order is executed at the open price of the next day.
  • MarketOnClose: Order is executed at close price of current day.
  • Close: Order is executed at close of the current period. As the platform only supports daily data, this order type is same as MarketOnClose . This is also the DEFAULT order type on the platform
4. Position

A position tracks the value invested in an instrument. Position typically consists of underlying security, quantity of shares and average price to accumulate the quantity along with various other metrics. This is another fundamental block of any trading strategy. A trading strategy changes the position in an instrument based on various inputs.

To increase the position in an instrument, one places a BUY order and to decrease the position, a SELL order is required.


# Position Object
type Position
    securitysymbol::SecuritySymbol
    quantity::Int64
    averageprice::Float64
    totalfees::Float64
    lastprice::Float64
    lasttradepnl::Float64
    realizedpnl::Float64 
    totaltradedvolume::Float64
end
                                                
                                            

In addition to tracking the quantity and average price, it tracks the amount of fees to accumulate the position along with any realized and unrealized profit/loss in the position. It also tracks the total volume transacted.

5. Portfolio

This represents a collection of Positions. It tracks the positions at any given time in the trading strategy and is used to compute various important metrics of a trading strategy. Any change in Position is reflected in the portfolio and related metrics.


# Portfolio Object: All positions and aggregated metrics
type Portfolio
    positions::Dict
    metrics::PortfolioMetrics
end
                                                
                                            
6. Portfolio Metrics

Encapsulates various types to exposures and security counts to monitor the portfolio. All the exposures are defined below.


# Aggregated portfolio metrics
type PortfolioMetrics
    netexposure::Float64
    grossexposure::Float64
    shortexposure::Float64
    longexposure::Float64
    shortcount::Int
    longcount::Int
end
                                                
                                            
  • Net Exposure: Sum of all holdings in the portfolio
  • Gross Exposure: Sum of absolute of holdings in the portfolio
  • Short Exposure: Sum of all short holdings in the portfolio
  • Long Exposure: Sum of all long holdings in the portfolio where Holdings = Position Quantity x Last Price of Position
  • Short Count: Number of Position where Holdings are negative
  • Long Count: Number of Position where Holdings are positive
7. Account

Along with available cash, it tracks various other metrics like netvalue and leverage which are important to check the behavior and well functioning of the trading strategy.


# Account type
type Account
    cash::Float64
    netvalue::Float64
    leverage::Float64
    portfolio::Portfolio
end
                                                
                                            
  • Net Value: Net Exposure of Portfolio + Cash
  • Leverage: (Gross Exposure of Portfolio + Cash ) / Net Value
8. Algorithm State

This is a very useful entity as it encapsulates Account, Portfolio Performance along with a parameter dictionary. The user can use this object to directly inspect the various encapsulated entities. In addition, the parameter dictionary can be used to add parameters persistent in a strategy.


# AlgorithmState type
type AlgorithmState
    account::Account
    performance::Performance
    params::Dict
end
                                                
                                            

II. User Defined Functions

For successful investment strategy, user is required to create two mandatory functions. These two functions are building blocks of every investment strategy.

a.initialize()

Initializes basic parameters of the strategy like backtesting period, initial cash and various other settings.


#Security Symbol: Combination of string ticker and an integer identifier
function initialize(state)
setstartdate("01/01/2015") 
setenddate("24/12/2015") 
setcash(100000.0) 
setuniverse(["TCS","WIPRO"]) 
end

                                                
                                        
b.ondata()

This function is called every time stamp or start of rebalancing period and details the logic of the investment strategy.


function ondata(data, state)
    #Get Universe
    universe = getuniverse()
    
    #get number of stocks in the universe
    nstocks = length(universe)
    
    #Allocate equal wealth to stock in the universe 
    for stock in universe
        setholdingpct(stock, 1.0/nstocks)
    end
end
                                                    
                                            

III. Algorithm State and Data

Above in the User Defined functions, two objects are passed to convey strategy information to the user. These two entities help in inspecting the latest data for securities in the Universe as well data about the strategy in general.

a.AlgorithmState

A user can use AlgorithmState object to inspect the account, portfolio positions and portfolio metrics. It also exposes rolling performance of last 252 days along with total returns of the algorithm. It can also be used to set persistent user defined parameters.

Below is an example that inspects the total cash in the account and adjusts a uniform portfolio to keep cash at a 5% of total portfolio value.


function ondata(data, state)
    cash  = state.account.cash
    netvalue = state.account.netvalue
    #Check if cash is less than 5% of the portfolio value
    if(cash < 0.05*netvalue)
        universe = getuniverse()
    
        #Allocate 95% of total wealth to stocks
        target_port = [(stock, 0.95/nstocks) for stock in universe)] 
        targetportfolio(target_port)
    end
end
                                                
                                            

Another example that sets user defined parameters in initialize() and uses it in ondata()


function initialize(state)
    #Set maximum leverage
    state["maxLeverage"] = 0.75
    
    #Set universe 
    setuniverse(["TCS"])  #Set universe
end
function ondata(data, state)
    pos = state.account.portfolio["TCS"]
    posValuePct = pos.quantitypos.lastprice/state.account.netvalue
    
    #Check if leverage exceeds maximum leverage
    if(posValuePct > state["maxLeverage"]) 
        setholdingpct("TCS", state["maxLeverage"])
    end
end
                                                
                                            

IV. Trading Environment

This section details the function to initialize the trading strategy. Most of the function described here can only be called from initialize() function

1. Set Start and End Dates

#API function to set start date of the backtest
setstartdate(date::Date)
setstartdate(date::String, format::String = "dd/mm/yyyy")
                                            
                                        

#API function to set end date of the backtest
setenddate(date::Date)
setenddate(date::String, format::String = "dd/mm/yyyy")
                                            
                                        
2. Set Cancel Policy: Function to set the cancel policy of the backtest

#API function to set cancel policy
setcancelpolicy(cancelpolicy::CancelPolicy)
setcancelpolicy(cancelpolicy::String)                                                
                                            
                                        

There are two cancel policies supported by the backtester

  • GTC (Good Till Canceled): In this cancel policy, a pending order stays active unless it's canceled by the user.
  • EOD (End of Day): In this cancel policy, all pending orders are closed just before the market close. This is the DEFAULT cancel policy of the backtester

#Example: Set the cancel policy to be Good Till Canceled
function initialize()
    setcancelpolicy(GTC)
    #OR
    setcancelpolicy("GTC")
end                                               
                                            
                                        
3. Set Commission: Function to set the commission model of the backtest

#API function to set commission model and value
setcommission(commission::Tuple)
setcommission(commission::Tuple)
                                            
                                        

There are two commission models supported by the backtester. It is called CommissionModel in the backtester

  • PerTrade: In this model, a fix percentage of the order value is charged as commission. If the order value is Rs. 10,000 and commission is 0.1% , then total commission for order is Rs. 10. This is the DEFAULT commission model in the backtester.
  • PerShare: In this model, a fix value per share is charged as commision. If an order consists of 300 shares and commission is 1 Paise per share, then total commission for order is Rs 3

#Example: Set commission to "PerShare" / 5 paise per share 
setcommission(PerShare, 0.05))
#OR
setcommission(("PerShare", 0.05))
end
                                            
                                        
4. Set Slippage: Function to set the slippage model of the backtest

What is Slippage? - Slippage is the difference between the trade execution price and the price where the strategy signaled the entry and exit for a trade (expected price)


#API function to set slippage model and value
setslippage(slippage::Tuple)
setslippage(slippage::Tuple)
                                            
                                        

There are two slippage models supported by the backtester. It is called SlippageModel in the backtester

  • Fixed: In this model, the difference between the model price an the execution price is a fixed value. If slippage is 0.50 and a buy order is placed at a expected price of Rs. 50, the execution price is Rs. 50.50.
  • Variable: In this model, the difference between the model price an the execution price is a percentage value of expected price. If slippage is 0.2% and a buy order is placed at a expected price of Rs. 50, the execution price is Rs. 51.00. This is the DEFAULT slippage model in the backtester.

#Example: Set slippage model to "Fixed" / 50 paise 
setslippage(Fixed, 0.50))
    #OR
    setslippage(("Fixed", 0.50))
end
                                            
                                        

V. Order Execution

This section details the functions available to place orders and adjust position value depending on strategy logic

1. Place Order: Function to send order to a brokerage for execution
#Functions to place order
#There are several different variety of place order functions
placeorder(order::Order)
placeorder(ticker::String, quantity::Int)
placeorder(security::Security, quantity::Int)
placeorder(symbol::SecuritySymbol, quantity::Int)
                                            
                                        

Using above function, an order can be placed for a security, a security symbol, or just the string ticker. In case of string ticker, a security is searched in data-stores (assuming defaults for exchange/country/security type)

2. Set Holding

These set of functions are useful when the backtester aims to modify the position directly without doing necessary calculation. These functions call placeorder functions under the hood. There are three major set of function to directly adjust the holding of an instrument based on a target. If target holding is different from the current holding, a BUY or SELL order is automatically generated to match the difference.

2a. setholdingpct: Function to set the holding in terms of percentage value

#To modify the holdings to 20% of the portfolio, 
#pass 0.2 as the target value


#If the actual holdings are less than 20%, 


#BUY order is automatically placed under the hood to adjust the holding percentage.
setholdingpct("TCS", 0.2)                                          
                                                
                                            

#List of all functions to set holdings in percentage
setholdingpct(ticker::String, target::Float64)
setholdingpct(symbol::SecuritySymbol, target::Float64)
setholdingpct(security::Security, target::Float64)                                               
                                                
                                            
2b. setholdingvalue: Function to set the holding in terms of monetary value

#To invest Rs. 20,000 in an instrument 
setholdingvalue("TCS", 20000)                                     
                                                
                                            

If the current holding is worth Rs. 10,000, it will automatically generate a BUY order worth Rs. 10,000 more under to hood to match the total value. In case, the holding is more than Rs. 20,000, a SELL order would be generated to reduce the position to the correct amount.


#List of all functions to set holdings in exact value
setholdingvalue(ticker::String, target::Float64)
setholdingvalue(symbol::SecuritySymbol, target::Float64)
setholdingvalue(security::Security, target::Float64)                                          
                                                
                                            
2c. setholdingshares: Function to set the holdings in terms of number of shares

#List of all functions to set holdings in shares
setholdingshares(ticker::String, target::Int)
setholdingshares(symbol::SecuritySymbol, target::Int)
setholdingshares(security::Security, target::Int)                                 
                                                
                                            

Sample use of above functions


#Examples: Invest 5% of the portfolio in TATAMOTORS
setholdingpct("TATAMOTORS", 0.05)
#Invest Rs. 30,000 in TATAMOTORS
setholdingvalue("TATAMOTORS", 30000)

#Invest worth 200 shares in TATAMOTORS
setholdingshares("TATAMOTORS", 200)                                   
                                                
                                            
3. Liquidate Holdings

These set of functions are useful when the backtester quickly wants to completely square off a position. The functionality can be achieved with other listed functions but the names of these functions helps in better legibility of a trading strategy.


#List of all functions to liquidate a holding
#Liquidates an instrument 
liquidate(ticker::String)
liquidate(symbol::SecuritySymbol)
liquidate(security::Security)

#Liquidates all the holdings in a portfolio
liquidateportfolio()
                                            
                                        
4. Set Target Portfolio

To achieve a target portfolio, use this function to avoid loops and improve legibility of the trading strategy.


#List of all functions to set target portfolio
#Pass portfolio as ticker or id dictionary (with weights as default values) 
settargetportfolio(port::Dict; scale = "wts")
settargetportfolio(port::Dict; scale = "wts")

#Pass portfolio as Array of tuples(with weights as default values)
settargetportfolio(port::Vector{Tuple{String, Float64}}; scale = "wts")
settargetportfolio(port::Vector{Tuple{Int64, Float64}}; scale = "wts")
settargetportfolio(port::Vector{Tuple{SecuritySymbol, Float64}}; scale = "wts")
settargetportfolio(port::Vector{Tuple{Security, Float64}}; scale = "wts")
                                            
                                        
5. Miscellaneous functions:

a. Cancel Order: Functions to cancel order by security or cancel all orders

b. Retrieve Order: Functions to retrieve open orders by security or all open orders in a trading strategy

#List of all functions to cancel/retrieve orders
#Retrieve order 
getopenorders()

#Cancel orders


#Cancel order by security
cancelopenorders(ticker::String)
cancelopenorders(symbol::SecuritySymbol)
cancelopenorders(security::Security)

#Cancel all open orders across all securities
cancelopenorders()
settargetportfolio(port::Vector{Tuple{Security, Float64}}; scale = "wts")
                                            
                                        

VI. Universe API

This section details the functions related to security universe API

  • Add Universe: Functions to add a security to the universe
    #Functions to place order
    #Functions to ADD security or collection of securities to already 
    #existing universe
    adduniverse(ticker::String)
    adduniverse(tickers::Vector)
                                                            
                                                        
  • Set Universe: Functions to set the universe to set collection of securities
    #Functions to place order
    #Functions to SET a security or collection of securities as the new universe
    setuniverse(ticker::String)
    setuniverse(tickers::Vector)
                                                            
                                                        
  • Get Universe: Function to retrieve the security collection in the trading strategy
    #Functions to place order
    #Function to GET the universe
    getuniverse()
                                                            
                                                        

VII. History API

This API helps in retrieving price history for a security or collection of securities. This will make a backbone for a lot of price history based investment strategies like Momentum, Extreme Reversal, Mean Reversion amongst other.

#Function to get price-history of stock 
history(tickers::Vector{String}, datatype::String, frequency::Symbol, horizon::Int)
history(securityids::Vector{Int64}, datatype::String, frequency::Symbol, horizon::Int)
history(symbols::Vector{SecuritySymbol}, datatype::String, frequency::Symbol, horizon::Int)
history(securities::Vector{Security}, datatype::String, frequency::Symbol, horizon::Int)
                                            
                                        

Important Points:

  • The above function returns TimeArray (TimeSeries) where columns names as tickers and row index as dates
  • Currently, the function only supports ':Day' frequency data
  • Supports 5 different datatypes, ['Open', 'High', 'Low', 'Close','Volume']
  • All the data is ADJUSTED for corporate actions

VIII. Utility API

This API helps in converting price series into more useful metrics like price returns, standard deviation and beta. This can be very useful API and help in creating complex strategies quickly

#Function to get metrics
#Function to compute Returns
price_returns(securities::Vector, series::String, frequency::Symbol; 
                        window::Int=22, total::Bool=false, rettype::Symbol=:log)

#Function to compute standard deviation
stddev(securities::Vector, series::String, frequency::Symbol; 
                            window::Int = 22, returns=true, rettype::Symbol=:log)
                            

#Function to compute beta
beta(securities::Vector, frequency::Symbol; window::Int = 252, 
                benchmark="NIFTY_50", rettype::Symbol=:log, series::String = "Close")

#where T is of type Security, SecuritySymbol, ticker(String) or securityid(Int)
                                            
                                        

Definitions and some optional parameters for metric functions:

  1. series: Type of price value. It can take one of the values in ["Open","High", "Low", "Close"]
  2. frequency: Sampling frequency of price series. Currently only daily (:Day) frequency is supported
  3. window: Length of historical horizon. The platform supports historical values since 2007 for NSE (National Stock Exchange of India)
  4. rettype: Type of returns series. It can take one of values in [:log, :simple]. DEFAULT is :log which denotes log returns

Other inputs for specific functions

  1. total in price_returns: If true, calculates the Returns of complete window and not every time stamp. DEFAULT is true
  2. returns in stddev: If true, calculates the standard deviation of returns (and not price). DEFAULT is true
  3. benchmark in beta: Security Ticker for the benchmark security. DEFAULT is NSE-50 Index denoted by "NIFTY_50"
Some Examples
  • Compute Standard Deviation of 66-days simple returns based on Open prices
    stddev_open_returns = UtitlityAPI.stddev(universe, "Open", :Day, window=66, rettype=:simple)
                                                                
                                                            
  • Compute Beta of stocks in universe over a window of 1 year (252 days) using simple returns and benchmark as NIFTY_100
    beta_close_ret = UtitlityAPI.beta(universe, :Day, window=252, rettype=:simple, benchmark="NIFTY_100")
                                                                
                                                            
  • Compute Daily price returns of stocks in universe for a period of 66 days
    open_price_returns = UtitlityAPI.price_returns(universe, "Open", :Day, window=66, rettype=:simple)
                                                                
                                                            

IX. Optimization API

Optimization API exposes some of the commonly used optimization methods used in investment management. The API can be used in the strategy by typing: using OptimizeAPI.

Currently, the optimization API solves for following types of problem

  1. series: Type of price value. It can take one of the values in ["Open","High", "Low", "Close"]
  2. frequency: Sampling frequency of price series. Currently only daily (:Day) frequency is supported
  3. window: Length of historical horizon. The platform supports historical values since 2007 for NSE (National Stock Exchange of India)
  4. rettype: Type of returns series. It can take one of values in [:log, :simple]. DEFAULT is :log which denotes log returns

Other inputs for specific functions

  1. Minimum absolute deviation
  2. Minimum semi-absolute deviation
  3. Mean-Variance
  4. Minimum volatility
  5. Minimum Loss
  6. Minimum Norm

The API exposes a generic function optimize which can be used to run a specific optimization via the parameter methodtype. The default type is Minimum Volatility. In addition, the API exposes various parameters to configure the common financial constraints and user defined linear restrictions. Some of the parameters are type specific and have no relevance in other types.

#Common API function
#Parameters are employed as required by the optimization type


#Default optimization type is minvol 
function optimize(symbols; 
            method::String="minvol") 
            initialportfolio::Vector=Vector(),
            constraints::Constraints=Constraints(),
            linearrestrictions::Vector=LinearRestriction[],
            window::Int=22, 
            targeret::Float64 = 0.2,
            riskaversion::Float64=1.0,
            returnsforecast::Vector=Vector(),
            roundbelow::Float64=0.0,
            cholesky::Bool=false)
                                        
                                    
Optimization Types

In addition to the generic optimize function, the API provides optimization routines via type specific naming convention.

screenshot
Minimum Volatility

In this optimization type, the objective is to minimize the volatility of the target portfolio. Volatility is measured by variance/covariance matrix of stock returns. The returns are calculated over a configurable window parameter.

#Common API function
#Parameters are employed as required by the optimization type


#Default optimization type is minvol 
function optimize(symbols; 
            method::String="minvol") 
            initialportfolio::Vector=Vector(),
            constraints::Constraints=Constraints(),
            linearrestrictions::Vector=LinearRestriction[],
            window::Int=22, 
            targeret::Float64 = 0.2,
            riskaversion::Float64=1.0,
            returnsforecast::Vector=Vector(),
            roundbelow::Float64=0.0,
            cholesky::Bool=false)
                                            
                                        

Method Specific Parameters:

  • nfactors: Number of factors when decomposing (using factor decomposition) the Covariance matrix (< number of stocks)
  • cholesky: To use Cholesky decomposition of Covariance matrix (default method is factor decomposition)
Mean Variance

There are two variants of this optimization problem. In the API, the two variants are called meanvar and meanvar2.

In the case of meanvar, the objective is to minimize the volatility of the target portfolio with a constraint on the portfolio target return.Mathematically, this problem can be represented as a

screenshot

where

  • w  is a vector of portfolio weights
  • Ω is the covariance matrix for the returns on the assets in the portfolio
  • R is a vector of expected returns.
  • w'Ωw is the variance of portfolio return.
  • R'w is the expected return on the portfolio.
  • μ is the target return of the portfolio
#Method Definition
function meanvariance(symbols; 
    initialportfolio::Vector=Vector(),
    constraints::Constraints=Constraints(),
    linearrestrictions::Vector=LinearRestriction[],
    window::Int=22, 
    roundbelow::Float64=0.0,
    returnsforecast::Vector=Vector(),
    nfactors::Int=10,
    cholesky::Bool=false)
end
                                                
                                            

Method Specific Parameters:

  • returnsforecast: Returns forecast (R) to compute the target returns (if not provided historical returns are used)
  • nfactors: Number of factors when decomposing (using factor decomposition) the Covariance matrix (< number of stocks)
  • cholesky: To use Cholesky decomposition of Covariance matrix (default method is factor decomposition)
Mean Variance2

In the case of meanvar2, the objective is to maximize the difference of target returns and volatility of the target portfolio. This optimization setup is more configurable via the riskaversion parameter which underline the importance of risk to an investor.

A value of 0 means investor doesn't care about the risk of the portfolio,
A value of 1.0 means investor is risk neutral;
A value greater than 1.0 means investor is increasingly risk sensitive


Mathematically, the problem is represented as

screenshot

where

  • w  is a vector of portfolio weights
  • Ω is the covariance matrix for the returns on the assets in the portfolio
  • R is a vector of expected returns.
  • w'Ωw is the variance of portfolio return.
  • R'w is the expected return on the portfolio.
  • λ is risk aversion parameter
#Method Definition
function meanvariance2(symbols; 
    initialportfolio::Vector=Vector(),
    constraints::Constraints=Constraints(),
    linearrestrictions::Vector=LinearRestriction[],
    window::Int=22, 
    roundbelow::Float64=0.0,
    returnsforecast::Vector=Vector(),
    nfactors::Int=10,
    cholesky::Bool=false)
end
                                                
                                            

Method Specific Parameters:

  • returnsforecast: Returns forecast (R) to compute the target returns (if not provided historical returns are used)
  • nfactors: Number of factors when decomposing (using factor decomposition) the Covariance matrix (< number of stocks)
  • cholesky: To use Cholesky decomposition of Covariance matrix (default method is factor decomposition)
Minimum (Semi) Absolute Deviation

These are other optimization types to minimize the risk of the portfolio. In these optimization setup, risk is measured in terms of (semi) absolute deviation of returns from the window mean. It is similar to Minimum Volatility in nature but doesn't consider the co-movement of stocks.

Absolute Deviation is defined as the mean of absolute difference of daily return from the window mean.
Semi-Absolute Deviation is defined as mean of absolute difference of daily return (only the values less than 0.0) from the window mean

#Method Definition
function minimumabsolutedeviation(symbols; 
    initialportfolio::Vector=Vector(),
    constraints::Constraints=Constraints(),
    linearrestrictions::Vector=LinearRestriction[],
    window::Int=22, 
    roundbelow::Float64=0.0)
end

function minimumsemiabsolutedeviation(symbols; 
    initialportfolio::Vector=Vector(),
    constraints::Constraints=Constraints(),
    linearrestrictions::Vector=LinearRestriction[],
    window::Int=22, 
    roundbelow::Float64=0.0)
end
                                            
                                        
Minimum Loss

In these optimization setup, the objective is to minimize the maximum possible loss of the portfolio. Mathematically, the problem can be written as

screenshot
#Method Definition
function minimumloss(symbols; 
    initialportfolio::Vector=Vector(),
    constraints::Constraints=Constraints(),
    linearrestrictions::Vector=LinearRestriction[],
    window::Int=22, 
    roundbelow::Float64=0.0)
end
                                            
                                        
Minimum Norm

In these optimization setup, the objective is to minimize the L2 norm of the portfolio deviation from a target portfolio (initial portfolio). This is a sort of a turnover minimizing optimization but instead of turnover (L1 norm), we use L2 norm.

#Method Definition
function minimumnorm(symbols; 
    initialportfolio::Vector=Vector(),
    constraints::Constraints=Constraints(),
    linearrestrictions::Vector=LinearRestriction[],
    roundbelow::Float64=0.0)
end
                                            
                                        
Constraints

Optimization technique are most useful when used within a realistic realm. For this, it is important to model the common portfolio and money management constraints. In the API, we expose pre-defined restriction using the Constraints object.

Constraints object support for following pre-defined linear restrictions

  1. Minimum Position Size
  2. Maximum Position Size
  3. Maximum Turnover Limit
  4. Minimum Portfolio Leverage
  5. Maximum Portfolio Leverage
  6. Maximum Trade Limit
  7. Minimum Long (Short) Exposure of Portfolio
  8. Maximum Long (Short) Exposure of Portfolio
  9. Minimum Position Size Per Security
  10. Maximum Trade Limit Per Security
#Default constraint object
#Defines default values of certain linear restrictions
Constraints(;minpositionsize::Float64 = 0.0, 
    maxpositionsize::Float64 = 0.2, 
    maxturnover::Float64 = 2.0, 
    tradelimit::Float64 = 1.0,
    minleverage::Float64 = 0.95,
    maxleverage::Float64 = 1.05,
    minlongexposure::Float64 = 0.0,
    maxlongexposure::Float64 = 1.0,
    minshortexposure::Float64 = 0.0,
    maxshortexposure::Float64 = 0.0,
    minpositionsizesecurity::Vector = Vector(),
    maxpositionsizesecurity::Vector = Vector(),
    tradelimitsecurity::Vector = Vector())
end
                                            
                                        
User-defined Linear Restrictions

Optimization technique are most useful when used within a realistic realm. For this, it is important to model the common portfolio and money management constraints. In the API, we expose pre-defined restriction using the Constraints object.

Constraints object only defines known linear restrictions and doesn't allow for user defined linear restrictions. LinearRestrictions object fills the gap and allows for any user-defined linear restrictions. All optimization types support for array/list of linear restrictions.

#Linear Restriction Object
type LinearRestriction
    coeff::Vector
    lower::Float64
    upper::Float64
end
                                            
                                        

It can be easily used to put portfolio level linear restrictions like Portfolio Beta etc.