This repository is used for a trading bot that applies convex optimization to make decisions, via the CVXPY library developed at Stanford.
As of now, it trades on fake money using an Alpaca Paper account that started with 100K dollars.
It also only has working stock/ETF trading, but not options trading (that will be ready soon).
The prev_versions folder contains previous end-to-end implementations of securities portfolio allocations in CVXPY.
Now the main directory has only two primary trader files, which highlight two different strategies: mean-variance optimization and conditional value-at-risk optimization.
An inspiration for my exploration of other strategies was this article by NVIDIA.
The files which implement those strategies are easily named mean_variance_trader.py and cvar_trader.py.
The basic mathematical formulation is given below, while a more deep dive is given in the "Trading Strategy" section below.
Both strategies solve for
The mean-variance optimizer solves for
using a quadratic program in the OSQP solver from CVXPY.
It also applies the Viterbi Algorithm for a Hidden Markov Model of the market regime for better risk management. Still tuning, but the last backtest resulted in a 48% annualized return with a sharpe ratio of 2.16.
The CVaR optimizer solves:
using a linear program, where:
The CVaR strategy had much more robust performance, with a maximum drawdown of 13% from 2023 to 2026, a Sharpe Ratio of 1.95, and a Calmar Ratio of 1.72.
In comparison, the mean-variance strategy had a maximum drawdown of nearly 30%, which is quite a lot of risk.
Alpaca SDK API keys are stored as environment variables and fetched using os.environ for safety.
Besides stock trading strategy explorations outlined above in all the cvx_trader_v(x).py, I have begun experiments with Black-Scholes options trading on the Alpaca inside black_scholes_options.py.
The script plot_risk_return.py assists in gauging the annualized risk-return trade-off between various assets. When plotting, one can observe that higher returns often lead to higher risk.
The Python script purge_helper.py is for helping to get rid of any shares with fractional value on Alpaca.
Two strategies were explored/researched/experimented with: Mean-Variance (Markowitz) portfolio optimization, and Conditional Value-at-Risk (CVaR) optimization.
The strategy used in the trading algorithm is mean-variance optimization. In any investment, we want to maximize gain with the least amount of risk within a certain trading period.
Judging an asset's ability to increase as well as its risk can be based on historical data, as well as current signals.
For example, the picture below is an annualized risk-return plot for a large number of assets from January 2024 to September 2025. Notice that higher gain tends to come with more risk, although there are some exceptions.
From the various market signals, the algorithm simply determines portfolio allocation to various assets. If we set the parameters to be more strongly risk-averse, then higher proportions will be allocated to lower-risk assets with maximum possible returns. Similarly, when we are more risk-tolerant, the algorithm will be willing to allocate more to assets with more risk, while seeking the best ROI.
In essence, the optimization is somewhat multi-objective.
While the previous mean-variance optimization portfolio managed risk via covariance, it treated all deviations from the mean as "bad", whereas proper portfolios only penalize downside risk.
Conditional Value-at-Risk (CVaR), also called Expected Shortfall, is a statistical measure that focuses only on the tail of the loss distribution.
For a given confidence level
VaR tells you a threshold, but not the expected loss beyond that threshold.
CVaR is the average loss in the worst
- Intuition: "If things go badly beyond the 95% worst-case threshold, the expected loss is CVaR."
Rockafellar and Uryasev showed that CVaR can be expressed as a convex optimization problem, in fact as a linear program, making it easy to solve and computationally tractable.