| Overall Statistics |
|
Total Orders 1 Average Win 0% Average Loss 0% Compounding Annual Return 25.448% Drawdown 10.000% Expectancy 0 Start Equity 100000 End Equity 157437.99 Net Profit 57.438% Sharpe Ratio 1.152 Sortino Ratio 1.397 Probabilistic Sharpe Ratio 81.100% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha -0 Beta 1.001 Annual Standard Deviation 0.107 Annual Variance 0.011 Information Ratio -0.075 Tracking Error 0.004 Treynor Ratio 0.123 Total Fees $1.35 Estimated Strategy Capacity $1400000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X Portfolio Turnover 0.14% |
from joblib import Parallel, delayed
import numpy as np
from AlgorithmImports import *
#################################################################
# VERY SIMPLE TRADING LOGIC TO TEST OUT PARALLEL THREADING:
# Calculates overall mean of the chunk means
# Compares this to the current closing price
# Generates buy signal if mean > current price
# Takes full position (100%) when signal is positive (if not invested)
# Holds position until next signal
# NO SELL LOGIC IN THIS BASIC EXAMPLE
#(it will only take one trade when it is below the 100 day close mean)
#################################################################
class ParallelProcessingAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2023, 1, 1)
self.SetEndDate(2025, 1, 1)
self.SetCash(100000)
self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
def parallel_calculation(self, data_chunk):
return np.mean(data_chunk)
def OnData(self, data):
if not self.Portfolio.Invested:
spyhist = self.History(self.spy, 100, Resolution.Daily) #100 days of SPY closing prices
# split the data into 4 equal chunks for parallel processing:
# aka 100 days of SPY Closing prices will look like:
#
# [100] --> [25][25][25][25] --> ...
# variables: spyhist data_chunks SEE LINE 46
#
data_chunks = np.array_split(spyhist['close'].values, 4)
#################### Using Threading Backend: ####################
# Breakdown of joblib functions
# Parallel: Manages thread pool and task distribution
# delayed: Postpones function execution until thread is ready
# n_jobs=2: Two worker threads since small example
# backend="threading": Supports threading or multiprocessing
###################################################################
results = Parallel(n_jobs=2, backend="threading")( # create 2 parallel threads (n_jobs=2)
delayed(self.parallel_calculation)(chunk) # calculates mean price for each chunk
for chunk in data_chunks # each thread processes different data chunks simultaneously
)
# -->: --> thread1: [25][25] : --> [ mean values ]
# --> thread2: [25][25] [ forall 100 days ]
# variables: (n_jobs=2) results
if np.mean(results) > spyhist['close'].iloc[-1]:
self.SetHoldings(self.spy, 1.0)