A comprehensive Python library for financial data processing, trading strategies, and emulation
Project description
warspite-financial
A comprehensive Python library for financial data processing, trading strategies, and emulation.
Overview
The warspite_financial library provides a modular framework for:
- Loading financial data from various providers (Yahoo Finance, OANDA, synthetic data)
- Applying trading strategies to market data
- Simulating trading scenarios with historical data
- Executing real trades through supported trading providers
- Visualizing trading results and performance metrics
Installation
From PyPI
pip install warspite-financial
After installation, the CLI is available as a console command:
warspite-financial-cli --help
With Optional Dependencies
# For additional data providers
pip install warspite-financial[providers]
# For development
pip install warspite-financial[dev]
# For everything
pip install warspite-financial[dev,providers]
Development Setup
- Clone the repository:
git clone <repository-url>
cd warspite-financial
- Create and activate a virtual environment:
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
- Install in development mode:
pip install -e .[dev,providers]
Quick Start
Using the Console Script (Easiest)
After installation, you can immediately start the CLI:
# Start interactive CLI
warspite-financial-cli
# Or with demo trading
warspite-financial-cli --provider demo
Using Python API
Basic Backtesting Example
from warspite_financial import (
BrownianMotionProvider,
SMAStrategy,
WarspiteTradingEmulator,
create_dataset_from_provider,
run_strategy_backtest
)
from datetime import datetime, timedelta
# Create synthetic data for testing
provider = BrownianMotionProvider()
end_date = datetime.now()
start_date = end_date - timedelta(days=60)
# Load data
dataset = create_dataset_from_provider(
provider=provider,
symbols=['BM-AAPL'], # BM- prefix for synthetic data
start_date=start_date,
end_date=end_date
)
# Create and test a strategy
strategy = SMAStrategy(period=20)
result = run_strategy_backtest(
dataset=dataset,
strategy=strategy,
initial_capital=10000
)
print(f"Final portfolio value: ${result.final_portfolio_value:.2f}")
print(f"Total return: {((result.final_portfolio_value - 10000) / 10000) * 100:.2f}%")
Using Real Data (with yfinance)
from warspite_financial import YFinanceProvider
from datetime import datetime, timedelta
# Create provider for real data
provider = YFinanceProvider()
# Load real market data
dataset = create_dataset_from_provider(
provider=provider,
symbols=['AAPL'],
start_date=datetime.now() - timedelta(days=30),
end_date=datetime.now()
)
# Run the same strategy on real data
result = run_strategy_backtest(dataset, SMAStrategy(period=10))
Visualization
from warspite_financial import create_visualization
# Create matplotlib chart
chart = create_visualization(
dataset=dataset,
renderer_type='matplotlib',
title='Stock Price Analysis'
)
chart.show()
# Create ASCII chart for terminal
ascii_chart = create_visualization(
dataset=dataset,
renderer_type='ascii'
)
print(ascii_chart)
Forecasting
The warspite_financial library includes heuristic forecasting capabilities for predicting future price movements based on historical patterns.
Basic Forecasting
from warspite_financial import (
WarspiteHeuristicForecaster,
create_dataset_from_provider,
BrownianMotionProvider
)
from datetime import datetime, timedelta
# Create historical dataset
provider = BrownianMotionProvider()
end_date = datetime.now()
start_date = end_date - timedelta(days=60)
dataset = create_dataset_from_provider(
provider=provider,
symbols=['BM-AAPL'],
start_date=start_date,
end_date=end_date
)
# Create forecaster
forecaster = WarspiteHeuristicForecaster(dataset)
# Generate 10-day forecast using linear extrapolation
forecast = forecaster.forecast(periods=10, method='linear')
print(f"Forecast generated for {len(forecast.timestamps)} periods")
print(f"Forecast symbols: {forecast.symbols}")
Forecasting Methods
The library supports multiple forecasting methods:
# Linear extrapolation (trend-based)
linear_forecast = forecaster.forecast(periods=5, method='linear')
# Exponential smoothing (weighted recent data)
exponential_forecast = forecaster.forecast(periods=5, method='exponential')
# Seasonal pattern recognition
seasonal_forecast = forecaster.forecast(periods=5, method='seasonal')
Confidence Intervals
Get confidence intervals for forecast reliability:
# Generate forecast
forecast = forecaster.forecast(periods=7, method='linear')
# Get confidence intervals for the forecast
confidence_intervals = forecaster.get_confidence_intervals()
print(f"Confidence intervals shape: {confidence_intervals.shape}")
print("Forecast includes uncertainty bounds for risk assessment")
Forecasting with Trading Strategies
Combine forecasting with trading strategies for forward-looking analysis:
from warspite_financial import SMAStrategy, WarspiteTradingEmulator
# Generate forecast
forecast = forecaster.forecast(periods=15, method='exponential')
# Apply strategy to forecast data
strategy = SMAStrategy(period=5) # Use shorter period for forecast data
emulator = WarspiteTradingEmulator(
dataset=forecast,
initial_capital=10000,
trading_fee=0.001,
spread=0.0001
)
emulator.add_strategy(strategy)
# Run strategy on forecasted data
forecast_result = emulator.run_to_completion()
print(f"Forecasted strategy performance: {forecast_result.total_return:.2%}")
print(f"Projected final value: ${forecast_result.final_portfolio_value:.2f}")
Combined Historical and Forecast Analysis
Analyze both historical performance and future projections:
# Step 1: Backtest on historical data
historical_strategy = SMAStrategy(period=10)
historical_result = run_strategy_backtest(
dataset=dataset,
strategy=historical_strategy,
initial_capital=10000
)
# Step 2: Generate forecast
forecast = forecaster.forecast(periods=10, method='linear')
# Step 3: Apply strategy to forecast using historical result as starting capital
forecast_emulator = WarspiteTradingEmulator(
dataset=forecast,
initial_capital=historical_result.final_portfolio_value,
trading_fee=0.001,
spread=0.0001
)
forecast_emulator.add_strategy(SMAStrategy(period=5))
forecast_result = forecast_emulator.run_to_completion()
print("=== Combined Analysis ===")
print(f"Historical return: {historical_result.total_return:.2%}")
print(f"Forecasted return: {forecast_result.total_return:.2%}")
print(f"Total projected value: ${forecast_result.final_portfolio_value:.2f}")
Forecasting Visualization
Visualize forecasts alongside historical data:
from warspite_financial import create_visualization
# Create visualization of forecast
forecast_chart = create_visualization(
dataset=forecast,
renderer_type='matplotlib',
title='Price Forecast - Next 10 Days'
)
forecast_chart.show()
# ASCII visualization for terminal
ascii_forecast = create_visualization(
dataset=forecast,
renderer_type='ascii'
)
print("=== Forecast Visualization ===")
print(ascii_forecast)
Forecasting Best Practices
- Use Sufficient Historical Data: Ensure your dataset has enough historical data for reliable pattern recognition
- Choose Appropriate Methods: Linear for trending markets, exponential for volatile markets, seasonal for cyclical patterns
- Consider Confidence Intervals: Always assess forecast uncertainty
- Validate with Backtesting: Test forecasting methods on historical data first
- Combine with Risk Management: Use forecasts as one input among many for trading decisions
Forecasting Limitations
- Heuristic Methods: These are pattern-based forecasts, not predictive models
- Market Uncertainty: Financial markets are inherently unpredictable
- Historical Bias: Past patterns may not continue in the future
- Use for Analysis: Forecasts should inform analysis, not drive automatic trading decisions
#### Complete Workflow Example
```python
from warspite_financial import basic_backtest_example
# Run a complete backtest with visualization
result = basic_backtest_example(
symbols=['BM-AAPL'],
days=30,
initial_capital=5000,
sma_period=15
)
print(f"Strategy performance: {result['total_return']:.2%}")
print(f"Number of trades: {result['num_trades']}")
# Display the chart
result['chart'].show()
Command Line Interface (CLI)
The warspite_financial library includes a powerful command-line interface for interactive trading and dataset visualization. The CLI provides both programmatic access and an interactive terminal mode.
Getting Started with CLI
from warspite_financial import WarspiteCLI
from warspite_financial.providers import OANDAProvider
# Create CLI without trading provider (visualization only)
cli = WarspiteCLI()
# Or with trading provider for live trading
provider = OANDAProvider(api_token="your_token", account_id="your_account")
cli = WarspiteCLI(trading_provider=provider)
Interactive Mode
Launch the interactive CLI for real-time trading operations:
# Start interactive mode programmatically
cli.run_interactive_mode()
Or use the console script directly from terminal:
# Start CLI in interactive mode
warspite-financial-cli
# Start with demo trading provider
warspite-financial-cli --provider demo
# Start with OANDA live trading (requires environment variables)
export OANDA_API_TOKEN=your_token_here
export OANDA_ENVIRONMENT=practice # or 'live'
warspite-financial-cli --provider oanda --account-id YOUR_ACCOUNT
# Show help and available options
warspite-financial-cli --help
# Show version
warspite-financial-cli --version
Security Note: For OANDA live trading, API tokens should be set as environment variables rather than command line arguments to prevent credentials from appearing in shell history or process lists.
This opens an interactive terminal with the following commands:
Trading Commands
# View current positions
warspite> positions
# Place buy orders
warspite> buy EURUSD 1000
warspite> buy AAPL 10 limit
# Place sell orders
warspite> sell EURUSD 500
warspite> sell AAPL 5 market
# Close specific positions
warspite> close POS_12345
# Close all positions
warspite> close all
# Check account status
warspite> account
warspite> status
Dataset Visualization
# Show current dataset info
warspite> dataset
# Render ASCII chart of loaded dataset
warspite> dataset render
Information Commands
# List available trading symbols
warspite> symbols
# Get help on commands
warspite> help
warspite> help buy
# Clear screen
warspite> clear
# Exit interactive mode
warspite> quit
ASCII Dataset Rendering
The CLI can render datasets as ASCII charts directly in the terminal:
from warspite_financial import create_dataset_from_provider, BrownianMotionProvider
from datetime import datetime, timedelta
# Create sample dataset
provider = BrownianMotionProvider()
dataset = create_dataset_from_provider(
provider=provider,
symbols=['BM-AAPL'],
start_date=datetime.now() - timedelta(days=30),
end_date=datetime.now()
)
# Load dataset into CLI
cli.set_current_dataset(dataset)
# Render ASCII chart
cli.render_dataset_ascii(dataset, width=80, height=20)
Example ASCII Output:
๐ ASCII Chart - Dataset Visualization
================================================================================
BM-AAPL Price
Range: 95.2341 - 104.7829
104.7829 โโโโ โ
103.1234 โ โโ โโโโ
101.4639 โ โโ โโโโโโโโ
99.8044 โ โโโ โโโโโโโ
98.1449 โ โโโโ โโโโโโ
96.4854 โ โโโโโ โโโโโโ
94.8259 โ โโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
11/26 12/26
================================================================================
Position Management
The CLI provides comprehensive position management with safety features:
# Open positions with validation
success = cli.open_position("EURUSD", 1000.0, order_type="market")
# Close specific position with confirmation
success = cli.close_position("POS_12345")
# Close all positions with safety prompt
success = cli.close_all_positions()
# Get detailed position information
positions = cli.get_positions()
CLI Safety Features
The CLI includes several safety mechanisms for live trading:
- Risk Validation: Warns when order size exceeds account balance
- Confirmation Prompts: Requires confirmation for large trades or losses
- Position Limits: Prevents excessive position sizes
- Error Handling: Graceful handling of connection and API errors
- Real-time Feedback: Immediate confirmation of trade execution
Example CLI Session
from warspite_financial import WarspiteCLI
from warspite_financial.providers import OANDAProvider
import os
# Set up environment variables for security
os.environ['OANDA_API_TOKEN'] = 'your_demo_token'
os.environ['OANDA_ENVIRONMENT'] = 'practice'
# Set up CLI with trading provider
provider = OANDAProvider(
api_token=os.environ['OANDA_API_TOKEN'],
account_id="demo_account",
environment=os.environ['OANDA_ENVIRONMENT']
)
cli = WarspiteCLI(trading_provider=provider)
# Start interactive session
cli.run_interactive_mode()
Or using the console script:
export OANDA_API_TOKEN=your_demo_token
export OANDA_ENVIRONMENT=practice
warspite-financial-cli --provider oanda --account-id demo_account
Interactive Session:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Warspite Financial CLI โ
โ Interactive Trading Interface โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Type 'help' or '?' to list commands.
warspite> status
โ
Trading provider: OANDAProvider
Account ID: demo_account
Balance: 10000.00 USD
Status: Connected
warspite> buy EURUSD 1000
๐ Placing BUY order for 1000.0 of EURUSD...
โ
Order executed successfully!
Order ID: ORDER_001
Action: BUY
Symbol: EURUSD
Quantity: 1000.0
warspite> positions
======================================================================
CURRENT POSITIONS
======================================================================
Account: demo_account
Balance: 9998.50 USD
Timestamp: 2023-12-26 14:30:15
----------------------------------------------------------------------
Position ID Symbol Quantity Price P&L %
----------------------------------------------------------------------
POS_001 EURUSD 1000.0000 1.2000 25.00 2.08%
----------------------------------------------------------------------
Total P&L: 25.00 0.25%
======================================================================
warspite> quit
๐ Goodbye!
Programmatic CLI Usage
You can also use CLI methods programmatically:
# Check if trading provider is connected
if cli._trading_provider:
# Get current positions
positions = cli.get_positions()
# Open a position
if cli.open_position("EURUSD", 500.0):
print("Position opened successfully")
# Render current dataset if loaded
if cli.get_current_dataset():
cli.render_dataset_ascii(cli.get_current_dataset())
CLI Configuration
The CLI can be customized for different use cases:
# CLI for visualization only (no trading)
viz_cli = WarspiteCLI()
viz_cli.set_current_dataset(your_dataset)
viz_cli.render_dataset_ascii(your_dataset, width=120, height=30)
# CLI with different trading providers
from warspite_financial.providers import YFinanceProvider
# Note: YFinanceProvider doesn't support trading, only data
data_provider = YFinanceProvider()
data_cli = WarspiteCLI() # No trading functionality
# For live trading, use TradingProvider implementations
trading_cli = WarspiteCLI(trading_provider=oanda_provider)
Features
Core Components
- Minimal Dependencies: Core functionality requires only numpy, pandas, and matplotlib
- Extensible Provider System: Support for multiple data sources with a common interface
- Strategy Framework: Implement custom trading strategies with a simple interface
- Trading Emulation: Backtest strategies with realistic trading costs and constraints
- Live Trading: Execute strategies with real money through supported brokers (OANDA)
- Command Line Interface: Interactive terminal interface for trading and visualization
- Visualization: Generate charts and performance reports in multiple formats
Available Providers
- BrownianMotionProvider: Synthetic data generation for testing (always available)
- YFinanceProvider: Yahoo Finance data (requires
yfinancepackage) - OANDAProvider: OANDA forex data and live trading (requires API credentials)
Built-in Strategies
- SMAStrategy: Simple Moving Average crossover strategy
- RandomStrategy: Random trading for baseline comparison
- PerfectStrategy: Optimal strategy with future knowledge (for benchmarking)
Visualization Options
- MatplotlibRenderer: Interactive charts with matplotlib
- ASCIIRenderer: Terminal-based charts
- PDFRenderer: Export charts to PDF
- CSVRenderer: Export data to CSV format
Architecture
The library follows a modular architecture with clear separation between:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ User Interface Layer โ
โ (Examples, Convenience Functions) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Strategy Layer โ
โ (SMAStrategy, RandomStrategy, PerfectStrategy) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Emulation Layer โ
โ (WarspiteTradingEmulator) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Dataset Layer โ
โ (WarspiteDataset) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Provider Layer โ
โ (BaseProvider, YFinanceProvider, OANDAProvider) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Advanced Usage
Custom Strategy Implementation
from warspite_financial import BaseStrategy
import numpy as np
class CustomStrategy(BaseStrategy):
def __init__(self, threshold=0.02):
self.threshold = threshold
def generate_positions(self, dataset):
# Implement your trading logic
prices = dataset.data[:, 3] # Close prices
returns = np.diff(prices) / prices[:-1]
positions = np.zeros(len(dataset.timestamps))
for i in range(1, len(positions)):
if returns[i-1] > self.threshold:
positions[i] = 1.0 # Long position
elif returns[i-1] < -self.threshold:
positions[i] = -1.0 # Short position
return positions
def get_parameters(self):
return {'threshold': self.threshold}
Live Trading (OANDA)
from warspite_financial import OANDAProvider, live_trading_example
# Set up OANDA provider with credentials
provider = OANDAProvider(
api_token="your_api_token",
account_id="your_account_id"
)
# Run live trading example (dry run by default)
result = live_trading_example(
trading_provider=provider,
symbols=['EUR_USD'],
strategy_type='sma',
dry_run=True # Set to False for real trading
)
Multi-Strategy Comparison
from warspite_financial import multi_strategy_comparison
# Compare multiple strategies
comparison = multi_strategy_comparison(
symbols=['BM-AAPL', 'BM-GOOGL'],
days=60,
initial_capital=10000
)
print(f"Best strategy: {comparison['best_strategy']}")
for name, result in comparison['results'].items():
print(f"{name}: {result['total_return']:.2%}")
Development
Running Tests
# Run all tests
python3 -m pytest
# Run specific test categories
python3 -m pytest -m unit
python3 -m pytest -m property
python3 -m pytest -m integration
# Run with coverage
python3 -m pytest --cov=warspite_financial
Test Categories
- Unit Tests: Test individual components and functions
- Property Tests: Property-based testing with Hypothesis for comprehensive input coverage
- Integration Tests: End-to-end workflow testing
API Reference
Core Classes
WarspiteDataset: Time-series financial data containerWarspiteTradingEmulator: Trading simulation engineWarspiteCLI: Command-line interface for interactive trading and visualizationBaseProvider: Abstract base class for data providersBaseStrategy: Abstract base class for trading strategies
Convenience Functions
create_dataset_from_provider(): Create datasets from providersrun_strategy_backtest(): Run complete strategy backtestscreate_visualization(): Generate charts and visualizations
Example Workflows
basic_backtest_example(): Complete backtesting workflowmulti_strategy_comparison(): Compare multiple strategiesvisualization_example(): Demonstrate visualization capabilitieslive_trading_example(): Live trading integration example
Error Handling
The library provides comprehensive error handling with custom exception types:
WarspiteError: Base exception for all library errorsProviderError: Data provider related errorsDatasetError: Dataset operation errorsStrategyError: Strategy execution errorsEmulatorError: Trading emulation errorsTradingError: Live trading errorsVisualizationError: Chart generation errors
Performance Considerations
- Core data operations use numpy arrays for efficiency
- Datasets support time-based slicing for large data analysis
- Property-based tests run with 100+ iterations for thorough validation
- Visualization renderers support multiple output formats
License
MIT License - see LICENSE file for details.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Changelog
Version 0.1.0
- Initial release with core functionality
- Provider system with BrownianMotion, YFinance, and OANDA support
- Strategy framework with SMA, Random, and Perfect strategies
- Trading emulation with cost modeling
- Command-line interface with interactive trading and ASCII visualization
- Visualization system with multiple renderers
- Comprehensive test suite with property-based testing
- End-to-end workflow examples
Support
- Documentation: Read the Docs
- Issues: GitHub Issues
- Repository: GitHub
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file warspite_financial-0.1.0.tar.gz.
File metadata
- Download URL: warspite_financial-0.1.0.tar.gz
- Upload date:
- Size: 80.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6dd40e3df88e4e302adb1d0a81a179e496ac910aebd4386cffda05fcdecd9c6c
|
|
| MD5 |
bb35d86dae3d19a788b933952e2de0ac
|
|
| BLAKE2b-256 |
87daae84f9d76846872e169c945da06db6cbac07210cf25a151fd919739ad19e
|
Provenance
The following attestation bundles were made for warspite_financial-0.1.0.tar.gz:
Publisher:
build.yml on mirror12k/warspite-financial-lib
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
warspite_financial-0.1.0.tar.gz -
Subject digest:
6dd40e3df88e4e302adb1d0a81a179e496ac910aebd4386cffda05fcdecd9c6c - Sigstore transparency entry: 788284963
- Sigstore integration time:
-
Permalink:
mirror12k/warspite-financial-lib@9a0218f373ade44be8ac5efdb4d27e1ede2c3b21 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/mirror12k
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build.yml@9a0218f373ade44be8ac5efdb4d27e1ede2c3b21 -
Trigger Event:
push
-
Statement type:
File details
Details for the file warspite_financial-0.1.0-py3-none-any.whl.
File metadata
- Download URL: warspite_financial-0.1.0-py3-none-any.whl
- Upload date:
- Size: 95.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c3dcd38bf855031abe890cabd0629ca56b8751cf01dbb864e42fd2f61353993
|
|
| MD5 |
65448ad36003dcb624a34065a5dbacc3
|
|
| BLAKE2b-256 |
bdd75efa9bea1a16a4b15710aada5a363e43515d06966d8d3a85362c6efa3ff9
|
Provenance
The following attestation bundles were made for warspite_financial-0.1.0-py3-none-any.whl:
Publisher:
build.yml on mirror12k/warspite-financial-lib
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
warspite_financial-0.1.0-py3-none-any.whl -
Subject digest:
4c3dcd38bf855031abe890cabd0629ca56b8751cf01dbb864e42fd2f61353993 - Sigstore transparency entry: 788284972
- Sigstore integration time:
-
Permalink:
mirror12k/warspite-financial-lib@9a0218f373ade44be8ac5efdb4d27e1ede2c3b21 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/mirror12k
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
build.yml@9a0218f373ade44be8ac5efdb4d27e1ede2c3b21 -
Trigger Event:
push
-
Statement type: