From Research to Production: Complete Strategy Development Workflow
This document provides a complete end-to-end example showing how to go from a strategy idea through backtesting, optimization, and validation, all the way to live trading deployment, covering the entire lifecycle of a quantitative trading strategy.
Full-Process AI Assistance
Every stage of this workflow can be completed with the help of an AI coding assistant. After installing FinLab Skill, AI can assist with writing code, interpreting performance, and troubleshooting errors from strategy conception to live deployment.
Workflow Overview
graph TD
A[Strategy Idea] --> B[Data Exploration]
B --> C[Strategy Development]
C --> D[Strategy Optimization]
D --> E[In-Depth Analysis]
E --> F[Out-of-Sample Testing]
F --> G{Validation Passed?}
G -->|Yes| H[Pre-Deployment Prep]
G -->|No| C
H --> I[Live Trading]
I --> J[Performance Tracking]
J --> K{Adjustment Needed?}
K -->|Yes| C
K -->|No| I
Stage 1: Strategy Conception & Initial Research
Strategy Hypothesis
We will develop a "Revenue Growth + Technical Breakout" combination strategy:
- Fundamentals: Revenue is accelerating recently (monthly moving average trending up)
- Technicals: Price breaks above moving averages, confirming momentum
- Risk Control: Exit when price drops below moving average, set 10% stop loss
Data Exploration
First, check whether the required data is complete:
from finlab import data
import pandas as pd
# Load required data
close = data.get('price:收盤價')
rev = data.get('monthly_revenue:當月營收')
# Check data range
print(f"Close-price range: {close.index[0]} ~ {close.index[-1]}")
print(f"Revenue range: {rev.index[0]} ~ {rev.index[-1]}")
# Check missing value ratio
print(f"Close-price missing: {close.isna().sum().sum() / close.size * 100:.2f}%")
print(f"Revenue missing: {rev.isna().sum().sum() / rev.size * 100:.2f}%")
# Output:
# Close-price range: 2007-04-23 ~ 2024-12-31
# Revenue range: 2000-01-01 ~ 2024-12-01
# Close-price missing: 2.15%
# Revenue missing: 8.73%
Stage 2: Strategy Development & Backtesting
Writing Strategy Logic
from finlab import data
from finlab.backtest import sim
# 1. Load data
close = data.get('price:收盤價')
rev = data.get('monthly_revenue:當月營收')
# 2. Calculate technical indicators
ma20 = close.average(20)
ma60 = close.average(60)
# 3. Calculate revenue indicators
rev_ma3 = rev.average(3) # Trailing 3-month average revenue
rev_ma12 = rev.average(12) # Trailing 12-month average revenue
# 4. Define entry conditions
entry_tech = (close > ma20) & (close > ma60) # Technicals: breakout above moving averages
entry_fund = (rev_ma3 / rev_ma12) > 1.1 # Fundamentals: revenue acceleration
entry_signal = entry_tech & entry_fund
# 5. Define exit conditions
exit_signal = close < ma20 # Break below 20-day MA
# 6. Combine entry/exit signals (with stop loss)
position = entry_signal.hold_until(
exit=exit_signal,
stop_loss=0.1 # Stop loss 10%
)
# 7. Backtest
report = sim(
position,
resample='M', # Monthly rebalance
position_limit=0.1, # Single-holding cap 10%
upload=False,
name="Revenue Growth Breakout v1.0"
)
# 8. Display performance
report.display()
Preliminary Performance Evaluation
stats = report.get_stats()
print(f"Annualized return: {stats['daily_mean']*100:.2f}%")
print(f"Sharpe ratio: {stats['daily_sharpe']:.2f}")
print(f"Max drawdown: {stats['max_drawdown']*100:.2f}%")
print(f"Win rate: {stats['win_ratio']*100:.2f}%")
# Example output:
# Annualized return: 15.23%
# Sharpe ratio: 0.87
# Max drawdown: -28.45%
# Win rate: 52.3%
Preliminary Assessment
- 15% annualized return is decent, but Sharpe ratio of 0.87 is somewhat low
- Max drawdown of -28% is acceptable
- Next step: optimize the strategy to improve risk-adjusted returns
Stage 3: Strategy Optimization
3.1 Use sim_conditions() to Test Condition Combinations
We add more candidate conditions to find the best combination:
from finlab.optimize.combinations import sim_conditions
# Original conditions
c1 = (close > ma20) & (close > ma60) # MA bullish
c2 = rev_ma3 / rev_ma12 > 1.1 # Revenue acceleration
# New conditions
pe = data.get('price_earning_ratio:本益比')
c3 = pe < 15 # Low P/E
c4 = close == close.rolling(20).max() # New 20-day high
c5 = rev / rev.shift(1) > 0.9 # Revenue MoM growth > -10%
# Conditions dictionary
conditions = {
'c1': c1, # MA bullish
'c2': c2, # Revenue acceleration
'c3': c3, # Low P/E
'c4': c4, # New high
'c5': c5 # Revenue MoM
}
# Test all combinations (31 total)
report_collection = sim_conditions(
conditions=conditions,
hold_until={'exit': exit_signal, 'stop_loss': 0.1},
resample='M',
position_limit=0.1,
upload=False
)
# Visualize comparison
report_collection.plot_stats('heatmap')
3.2 Analyze Optimization Results
# Get performance metrics table
stats_df = report_collection.get_stats()
# Find top 3 combinations
top3 = stats_df.T.sort_values('daily_sharpe', ascending=False).head(3)
print(top3[['daily_mean', 'daily_sharpe', 'max_drawdown', 'win_ratio']])
# Example output:
# daily_mean daily_sharpe max_drawdown win_ratio
# c1 & c2 & c3 0.182 1.35 -0.245 0.561
# c1 & c2 & c5 0.165 1.28 -0.267 0.548
# c1 & c2 0.152 0.87 -0.285 0.523
Optimization Results
The c1 & c2 & c3 (MA bull + Revenue acceleration + Low P/E) combination improved the Sharpe ratio from 0.87 to 1.35, a significant improvement!
3.3 Adopt the Best Combination
# Redefine strategy using the best combination
best_entry = c1 & c2 & c3
position_optimized = best_entry.hold_until(
exit=exit_signal,
stop_loss=0.1
)
report_opt = sim(
position_optimized,
resample='M',
position_limit=0.1,
upload=False,
name="Revenue Growth Breakout v2.0 (optimized)"
)
3.4 Optimize Stop Loss & Take Profit
Use MAE/MFE analysis to find optimal stop loss/take profit levels:
# Display volatility analysis
report_opt.display_mae_mfe_analysis()
# Get trade records
trades = report_opt.get_trades()
# Analyze MAE/MFE quantiles
mae_q75 = abs(trades['mae'].quantile(0.75))
gmfe_q75 = trades['gmfe'].quantile(0.75)
print(f"MAE Q75: {mae_q75*100:.2f}%") # e.g., 8.5%
print(f"GMFE Q75: {gmfe_q75*100:.2f}%") # e.g., 18.3%
# Adjust stop loss/take profit
position_final = best_entry.hold_until(
exit=exit_signal,
stop_loss=mae_q75 * 1.2, # Stop loss = MAE Q75 * 1.2 = 10.2%
take_profit=gmfe_q75 * 0.8 # Take profit = GMFE Q75 * 0.8 = 14.6%
)
report_final = sim(
position_final,
resample='M',
position_limit=0.1,
upload=False,
name="Revenue Growth Breakout v3.0 (final)"
)
Stage 4: In-Depth Analysis
4.1 Liquidity Risk Assessment
Check whether the strategy is suitable for large capital:
# Liquidity analysis (assuming 10M TWD capital, minimum 100K shares per entry/exit)
report_final.run_analysis('LiquidityAnalysis', required_volume=100000)
# Example output:
# buy_high sell_low low_volume_stocks disposition_stocks
# entry 0.032 0.008 0.087 0.012
# exit 0.015 0.045 0.092 0.008
Liquidity Review
- Low volume stock ratio of 8.7% -- large capital should be cautious
- Disposition stock ratio of 1.2% -- acceptable
- Recommend adding volume screening conditions
4.2 Annual Stability Analysis
Review strategy performance across different years:
report_final.run_analysis('PeriodStatsAnalysis')
# Output shows Sharpe ratio, return, volatility, etc. for each year
# Check if the strategy performs particularly poorly in certain years
4.3 Alpha/Beta Analysis
Measure the strategy's excess return:
report_final.run_analysis('AlphaBetaAnalysis')
# Displays three sections: overall summary, annual Alpha/Beta, recent Alpha/Beta
# Example output:
# Alpha: 8.20% (annualized excess return)
# Beta: 0.65 (market sensitivity)
To obtain raw values for further analysis:
result = report_final.run_analysis('AlphaBetaAnalysis', display=False)
print(f"Alpha: {result['overall']['alpha']:.2%}")
print(f"Beta: {result['overall']['beta']:.2f}")
Alpha/Beta Assessment
- Alpha of 8.2% is excellent, indicating genuine excess returns
- Beta of 0.65 means the strategy has lower volatility than the market
Stage 5: Out-of-Sample Testing
Split data into training and testing sets to verify the strategy is not overfit:
# Define split date
train_end = '2022-12-31'
test_start = '2023-01-01'
# Training set backtest (2018-2022)
position_train = position_final[position_final.index <= train_end]
report_train = sim(
position_train,
resample='M',
position_limit=0.1,
upload=False,
name="Training set (2018-2022)"
)
# Test set backtest (2023-2024)
position_test = position_final[position_final.index >= test_start]
report_test = sim(
position_test,
resample='M',
position_limit=0.1,
upload=False,
name="Test set (2023-2024)"
)
# Performance comparison
stats_train = report_train.get_stats()
stats_test = report_test.get_stats()
comparison = pd.DataFrame({
'Training': [
stats_train['daily_mean'],
stats_train['daily_sharpe'],
stats_train['max_drawdown'],
stats_train['win_ratio']
],
'Testing': [
stats_test['daily_mean'],
stats_test['daily_sharpe'],
stats_test['max_drawdown'],
stats_test['win_ratio']
]
}, index=['Annualized return', 'Sharpe ratio', 'Max drawdown', 'Win rate'])
print(comparison)
# Example output:
# Training Testing
# Annualized return 0.182 0.165
# Sharpe ratio 1.35 1.21
# Max drawdown -0.245 -0.267
# Win rate 0.561 0.542
Out-of-Sample Evaluation Criteria
| Metric | Training vs Testing | Assessment |
|---|---|---|
| Annualized Return | 18.2% vs 16.5% | Pass -- reasonable degradation (<20%) |
| Sharpe Ratio | 1.35 vs 1.21 | Pass -- slight decrease but still > 1 |
| Max Drawdown | -24.5% vs -26.7% | Pass -- small difference |
| Win Rate | 56.1% vs 54.2% | Pass -- stable |
Out-of-Sample Test Passed
Test set performance decreased slightly but remains excellent. The strategy is not overfit and can proceed to deployment!
Stage 6: Pre-Deployment Preparation
6.1 Upload Strategy to Cloud
# Upload the final version to the FinLab platform
report_final.upload(name="Revenue Growth Breakout v3.0")
# Get position list
position_info = report_final.position_info()
print(f"Next rebalance date: {position_info['next_trading_date']}")
# position_info keys are stock symbols (e.g., '2330 TSMC'), mixed with some metadata keys
# Filter out holdings (exclude metadata keys)
metadata_keys = {'update_date', 'next_trading_date', 'trade_at', 'freq', 'market', 'stop_loss', 'take_profit'}
stocks = {k: v for k, v in position_info.items() if k not in metadata_keys}
print(f"Current holdings count: {len(stocks)}")
print("Holdings list:")
for symbol, info in stocks.items():
print(f" {symbol}: weight={info['weight']:.2%}, status={info['status']}")
# Example output:
# Next rebalance date: 2025-01-31
# Current holdings count: 8
# Holdings list:
# 2330 TSMC: weight=12.00%, status=hold
# 2317 Foxconn: weight=11.00%, status=hold
# 2454 MediaTek: weight=10.00%, status=hold
# ...
6.2 Prepare Live Capital Allocation
# Assuming 1M TWD live capital
capital = 1000000
# Calculate the amount to invest in each stock (reusing stocks and metadata_keys from 6.1)
for symbol, info in stocks.items():
info['amount'] = info['weight'] * capital
print("Capital allocation:")
for symbol, info in stocks.items():
print(f" {symbol}: weight={info['weight']:.2%}, amount={info['amount']:,.0f}")
# Output:
# 2330 TSMC: weight=12.00%, amount=120,000
# 2317 Foxconn: weight=11.00%, amount=110,000
# ...
Stage 7: Live Trading
7.1 Set Up Broker Account (Sinopac Securities Example)
from finlab.online.sinopac_account import SinopacAccount
# Set up broker account (environment variables must be configured first; see live trading tutorial)
account = SinopacAccount()
Live Trading Precautions
- For first-time use, test with
view_only=Truefor simulated orders - Confirm broker API keys are correct
- Ensure sufficient funds and credit limits
- Start with small capital for testing
7.2 Calculate Target Positions
from finlab.online.order_executor import Position
# Calculate target positions (round lots)
capital = 1000000 # Live capital: 1,000,000 TWD
position = Position.from_report(report_final, capital)
# Or use odd lot trading
position = Position.from_report(report_final, capital, odd_lot=True)
print(position)
# Output: [{'stock_id': '2330', 'quantity': 5, 'order_condition': <OrderCondition.CASH: 1>}, ...]
7.3 Execute Orders
from finlab.online.order_executor import OrderExecutor
# Create order executor
order_executor = OrderExecutor(position, account=account)
# Order preview (view mode, no actual orders placed)
order_executor.create_orders(view_only=True)
# Execute orders (actual orders placed; test during market close for first-time use)
order_executor.create_orders()
# Update limit price (use last trade price as new limit price)
order_executor.update_order_price()
# Cancel all pending orders
order_executor.cancel_orders()
Stage 8: Performance Tracking
8.1 Retrieve Live Trading Records
# Get live positions
account_positions = account.get_position()
print(account_positions)
# Get account total value and cash
account_value = account.get_total_balance()
print(f"Account total value: {account_value:,.0f}")
8.2 Monitor Strategy Performance
# Periodically check account status (recommended after each market close)
import pandas as pd
# Record daily account value
daily_value = {
'date': pd.Timestamp.today(),
'account_value': account.get_total_balance(),
'positions': len(account.get_position())
}
print(f"Date: {daily_value['date']}")
print(f"Account value: {daily_value['account_value']:,.0f}")
print(f"Holdings count: {daily_value['positions']}")
# Compare with backtest
backtest_return = report_final.get_stats()['daily_mean']
print(f"Backtest annualized return: {backtest_return * 100:.2f}%")
8.3 Periodic Review
Recommended monthly review items:
# 1. Check if holdings follow the plan
current_stocks = set(account_positions['stock_id'])
metadata_keys = {'update_date', 'next_trading_date', 'trade_at', 'freq', 'market', 'stop_loss', 'take_profit'}
planned_stocks = {k for k in position_info if k not in metadata_keys}
print(f"Planned holdings: {planned_stocks}")
print(f"Actual holdings: {current_stocks}")
print(f"Mismatches: {planned_stocks.symmetric_difference(current_stocks)}")
# 2. Check if stop loss is triggered
for stock_id, pos in account_positions.items():
entry_price = pos['avg_price']
current_price = pos['current_price']
unrealized_pnl = (current_price / entry_price - 1)
if unrealized_pnl < -0.10: # Stop loss 10%
print(f"Warning: {stock_id} stop loss triggered, unrealized P&L: {unrealized_pnl*100:.2f}%")
Complete Code Summary
Below is the complete executable code:
# =============================================================================
# From Research to Production: Revenue Growth Breakout Strategy Complete Example
# =============================================================================
from finlab import data
from finlab.backtest import sim
from finlab.optimize.combinations import sim_conditions
# -----------------------------------------------------------------------------
# Stages 1 & 2: Data Loading & Strategy Development
# -----------------------------------------------------------------------------
# Load data
close = data.get('price:收盤價')
rev = data.get('monthly_revenue:當月營收')
pe = data.get('price_earning_ratio:本益比')
# Calculate indicators
ma20 = close.average(20)
ma60 = close.average(60)
rev_ma3 = rev.average(3)
rev_ma12 = rev.average(12)
# Define conditions
c1 = (close > ma20) & (close > ma60) # MA bullish
c2 = rev_ma3 / rev_ma12 > 1.1 # Revenue acceleration
c3 = pe < 15 # Low P/E
# Exit signal
exit_signal = close < ma20
# -----------------------------------------------------------------------------
# Stage 3: Strategy Optimization
# -----------------------------------------------------------------------------
# Test condition combinations
conditions = {'c1': c1, 'c2': c2, 'c3': c3}
report_collection = sim_conditions(
conditions=conditions,
hold_until={'exit': exit_signal, 'stop_loss': 0.1},
resample='M',
position_limit=0.1,
upload=False
)
# Find the best combination
stats_df = report_collection.get_stats()
top1 = stats_df.T.sort_values('daily_sharpe', ascending=False).index[0]
print(f"Best combination: {top1}")
# Use the best combination
best_entry = c1 & c2 & c3
# Optimize stop loss/take profit
position_temp = best_entry.hold_until(exit=exit_signal, stop_loss=0.1)
report_temp = sim(position_temp, resample='M', position_limit=0.1, upload=False)
trades = report_temp.get_trades()
mae_q75 = abs(trades['mae'].quantile(0.75))
gmfe_q75 = trades['gmfe'].quantile(0.75)
# Final strategy
position_final = best_entry.hold_until(
exit=exit_signal,
stop_loss=mae_q75 * 1.2,
take_profit=gmfe_q75 * 0.8
)
report_final = sim(
position_final,
resample='M',
position_limit=0.1,
upload=False,
name="Revenue Growth Breakout v3.0"
)
# -----------------------------------------------------------------------------
# Stage 4: In-Depth Analysis
# -----------------------------------------------------------------------------
# Liquidity analysis
report_final.run_analysis('LiquidityAnalysis', required_volume=100000)
# Volatility analysis
report_final.display_mae_mfe_analysis()
# Period stability
report_final.run_analysis('PeriodStatsAnalysis')
# Alpha/Beta
report_final.run_analysis('AlphaBetaAnalysis')
# -----------------------------------------------------------------------------
# Stage 5: Out-of-Sample Testing
# -----------------------------------------------------------------------------
train_end = '2022-12-31'
test_start = '2023-01-01'
position_train = position_final[position_final.index <= train_end]
position_test = position_final[position_final.index >= test_start]
report_train = sim(position_train, resample='M', position_limit=0.1, upload=False)
report_test = sim(position_test, resample='M', position_limit=0.1, upload=False)
print("Training set Sharpe:", report_train.get_stats()['daily_sharpe'])
print("Test set Sharpe: ", report_test.get_stats()['daily_sharpe'])
# -----------------------------------------------------------------------------
# Stage 6: Pre-Deployment Preparation
# -----------------------------------------------------------------------------
# Upload to cloud
report_final.upload(name="Revenue Growth Breakout v3.0")
# Get position list
position_info = report_final.position_info()
metadata_keys = {'update_date', 'next_trading_date', 'trade_at', 'freq', 'market', 'stop_loss', 'take_profit'}
stocks = {k: v for k, v in position_info.items() if k not in metadata_keys}
print("Next-period holdings:")
for symbol, info in stocks.items():
print(f" {symbol}: weight={info['weight']:.2%}")
# -----------------------------------------------------------------------------
# Stage 7: Live Trading (requires broker account setup)
# -----------------------------------------------------------------------------
# from finlab.online.sinopac_account import SinopacAccount
# from finlab.online.order_executor import OrderExecutor
#
# account = SinopacAccount() # Environment variables must be set first
# position = Position.from_report(report_final, 1000000)
# executor = OrderExecutor(position, account)
# executor.create_orders(view_only=True) # Test with view_only first
# -----------------------------------------------------------------------------
# Stage 8: Performance Tracking
# -----------------------------------------------------------------------------
# Periodically run the following code to review live status:
# account_value = account.get_total_value()
# print(f"Account total value: {account_value}")
Key Takeaways
Development Stage Checklist
- [ ] Clear strategy hypothesis: Has a clear logical basis (technical/fundamental/institutional)
- [ ] Data integrity check: Confirm data range is sufficient and missing value ratio is reasonable
- [ ] Acceptable initial backtest performance: Annualized return > 10%, Sharpe ratio > 0.5
- [ ] Condition combination optimization: Use
sim_conditions()to find the best combination - [ ] Stop loss/take profit optimization: Use MAE/MFE analysis to set reasonable levels
Validation Stage Checklist
- [ ] Liquidity risk: Low volume ratio < 20%, disposition stock ratio < 5%
- [ ] Annual stability: No consecutive years of extremely poor performance
- [ ] Alpha > 0: Strategy has excess returns
- [ ] Out-of-sample test passed: Test set performance degradation < 30%, Sharpe ratio still > 1
Deployment Stage Checklist
- [ ] Strategy uploaded to cloud: Can review position list anytime
- [ ] Broker account setup complete: API keys correct, permissions granted
- [ ] Tested with simulated orders first: Confirmed order logic is correct
- [ ] Small capital test: Tested with small amounts for 1-2 months
- [ ] Regular review mechanism: Set up monthly live performance review
Further Learning
After completing this workflow, you can explore:
- Machine Learning Strategy: ML Strategy Complete Workflow (Advanced)
- Multi-Strategy Portfolio: Multi-Strategy Portfolio Management (Advanced)
- Custom Analysis Modules: Strategy Analysis Module Guide
- Custom Market Backtesting: Custom Market Object Guide
Common Error Handling Checklist
When executing the complete workflow, make sure to perform error checks at each stage. Below are key checkpoints and solutions:
Stages 1-2: Data Loading & Backtesting
Common Errors:
- KeyError: 'price:收盤價' -- API token not set or incorrect data table name
- Data is empty or has too many missing values
- Backtest has no trade records (entry conditions too strict)
Validation Methods:
try:
# 1. Check data loading
close = data.get('price:收盤價')
if close.empty:
raise ValueError("❌ Data is empty; check your API token")
missing_ratio = close.isna().sum().sum() / close.size
if missing_ratio > 0.1:
print(f"⚠️ Warning: missing value ratio {missing_ratio:.1%} > 10%")
# 2. Check entry signals
entry_count = entry_signal.sum(axis=1).mean()
if entry_count < 1:
print("⚠️ Warning: entry conditions too strict; average < 1 stock per day")
print("Suggestion: loosen conditions or use .is_largest(N) for a fixed count")
# 3. Check backtest results
report = sim(position, resample='M', position_limit=0.1)
trades = report.get_trades()
if len(trades) == 0:
raise ValueError("❌ Strategy produced no trades; check your entry conditions")
print(f"✅ Backtest succeeded: {len(trades)} trades")
except KeyError as e:
print(f"❌ Dataset not found: {e}")
print("Visit https://ai.finlab.tw/database to confirm the dataset name")
print("Or verify your API token is correctly set")
except ValueError as e:
print(f"❌ Data validation failed: {e}")
Detailed Error Handling: See Data Download Error Handling
Stage 3: Strategy Optimization
Common Errors:
- sim_conditions() takes too long due to too many combinations
- Condition definitions are incorrect (inconsistent date ranges)
Validation Methods:
# Limit the number of combinations (< 50)
conditions = {
'c1': c1,
'c2': c2,
'c3': c3,
# Maximum 5 conditions (2^5 = 32 combinations)
}
# Estimate execution time
num_combinations = 2**len(conditions) - 1
print(f"Will test {num_combinations} combinations, estimated time: ~{num_combinations * 30 / 60:.1f} minutes")
if num_combinations > 100:
print("⚠️ Warning: too many combinations; test a subset of conditions first")
Detailed Error Handling: See Strategy Optimization Error Handling
Stage 5: Out-of-Sample Testing
Common Errors: - Training and test set date ranges overlap - Test set performance differs too much from training set (overfitting)
Validation Methods:
# Check date ranges
train_end = '2022-12-31'
test_start = '2023-01-01'
position_train = position_final[position_final.index <= train_end]
position_test = position_final[position_final.index >= test_start]
# Confirm no overlap
if position_train.index[-1] >= position_test.index[0]:
raise ValueError("❌ Training and test set dates overlap!")
# Check performance difference
sharpe_train = report_train.get_stats()['daily_sharpe']
sharpe_test = report_test.get_stats()['daily_sharpe']
degradation = (sharpe_train - sharpe_test) / sharpe_train
if degradation > 0.5:
print(f"⚠️ Warning: Sharpe ratio degradation {degradation:.1%} > 50%; possible overfitting")
print("Suggestions:")
print("1. Simplify strategy conditions")
print("2. Extend the training data range")
print("3. Use cross-validation")
Detailed Error Handling: See Backtest Error Handling
Stage 7: Live Trading
Common Errors: - Broker account connection failure (wrong API key, expired certificate) - Insufficient funds or too many positions - Orders rejected (disposition stocks not pledged, limit-up/down locked)
Safety Checklist:
# Pre-trading safety checks
def pre_trading_checks(position, account):
"""Pre-order safety checks for live trading"""
print("=== Live Trading Safety Checks ===\n")
# 1. Check number of positions
if len(position) > 30:
raise ValueError(f"❌ Too many positions ({len(position)}); recommended < 30")
# 2. Check available funds
available = account.get_balance()
required = position.total_value # Assumes this method exists
if required > available * 0.9:
raise ValueError(f"❌ Insufficient funds! Need {required:,.0f}, available {available:,.0f}")
# 3. Check account connection
try:
account.get_position()
except Exception as e:
raise ConnectionError(f"❌ Broker account connection failed: {e}")
print("✅ All checks passed\n")
# Execute checks
try:
pre_trading_checks(position_info, account)
# Preview first (no actual orders)
executor.create_orders(view_only=True)
# Execute after confirmation (uncomment)
# executor.create_orders()
except (ValueError, ConnectionError) as e:
print(f"\n{e}")
print("Fix the issues above before placing orders")
Detailed Error Handling: See Live Trading Error Handling
Final Reminder for Live Trading
Before placing orders, confirm the following:
- Strategy has passed out-of-sample testing
- Tested with a simulated account for at least 1 week
- Initial capital does not exceed 10-20% of total capital
- Stop loss mechanism is in place to avoid catastrophic losses
- Monitor positions and order status daily
Errors can lead to financial losses! Please proceed with caution!