Skip to content

memoryfraction/Quant.Infra.Net

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

241 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Quant.Infra.Net

.NET

Quant.Infra.Net is a .NET quantitative trading infrastructure library that lets you fetch data, run statistical analysis, execute trades, and send notifications with minimal code.


Version History

Version Date Description
1.0.0 2024-01-15 Initial release with core features: data acquisition, statistical analysis, trade execution, and notifications
1.1.0 2024-02-20 Added support for Schwab broker integration and enhanced portfolio performance metrics
1.2.0 2024-03-10 Improved Python integration stability and added new statistical analysis methods
1.3.0 2024-04-05 Enhanced notification services with email templates and improved error handling
1.4.0 2024-05-16 Updated API integrations to handle recent broker changes, added comprehensive documentation
1.5.0 2024-05-16 Enforced comprehensive code standards: SOLID principles, bilingual XML documentation, parameter validation, English-only runtime messages, UTC time standardization, centralized enum management, sensitive data protection, and created complete code standards documentation

Documentation Update Policy

This README follows a strict update policy to ensure consistency and accuracy:

  • Synchronization: Every code change that affects public APIs must be reflected in this document simultaneously in both English and Chinese sections.
  • Versioning: Version numbers follow Semantic Versioning (MAJOR.MINOR.PATCH) standards.
  • Change Log: Each version entry must include: version number, release date, and concise description of changes.
  • Bilingual Consistency: Both English and Chinese sections must maintain identical information and structure.
  • Last Updated: 2024-05-16 (Version 1.5.0)

English

Why Use This Library

When developing quantitative trading systems, you likely face these recurring problems:

Problem What this library provides
Stock / crypto data APIs are scattered with inconsistent formats Unified ITraditionalFinanceSourceDataService and ICryptoSourceDataService with standardized OHLCV models
Pair trading requires ADF test, OLS regression, Z-Score, correlation IAnalysisService provides ready-to-use methods in one line
Integrating Binance, Alpaca, etc. means rewriting boilerplate IBinanceUsdFutureService, IUSEquityBrokerService unified abstractions
No notification pipeline after strategy runs DingTalk IDingtalkService, WeChat IWeChatService, Email EmailService
CAGR, Sharpe, Calmar, max drawdown must be coded from scratch StrategyPerformanceAnalyzer with all metrics built in
Timers and rolling windows rewritten every project IntervalTrigger, RollingWindow<T> ready to use

In short: stop reinventing the wheel — focus on your strategy.

What Problems It Solves

  1. Data Acquisition — Download stock daily bars from Yahoo Finance, batch-download crypto klines from Binance, read local data from CSV/MySQL/MongoDB.
  2. Statistical Analysis — Correlation, ADF stationarity test, OLS regression, Z-Score, Shapiro-Wilk normality test, pair-trading spread calculation.
  3. Trade Execution — Binance futures order/liquidate, Alpaca US equity order/liquidate, with Testnet/Paper/Live environment switching.
  4. Notifications — DingTalk bot, WeChat Work webhook, personal/commercial bulk email.
  5. Portfolio & Performance — Portfolio snapshots, equity curve charting, CAGR/Sharpe/Calmar/MaxDrawdown calculation.
  6. Utilities — Rolling window, interval trigger, resolution conversion, DataFrame I/O.

Quick Start

Note on Data Sources

The C# package YahooFinanceApi cannot keep up with Yahoo Finance's frequent API changes and often returns 401 Unauthorized. The Python yfinance package is maintained more actively by a large community. This project uses pythonnet to call yfinance from C# via a local Anaconda virtual environment.

Step 1: Install

dotnet new console -n MyQuantApp
cd MyQuantApp
dotnet add package Quant.Infra.Net
dotnet add package pythonnet
dotnet add package Microsoft.Extensions.DependencyInjection

Or use a ProjectReference when developing inside the repo:

<ProjectReference Include="..\Quant.Infra.Net\Quant.Infra.Net.csproj" />

Step 2: Create a Python Virtual Environment (One-Time Setup)

Why Python? The C# package YahooFinanceApi cannot keep up with Yahoo Finance's frequent API changes and often returns 401 Unauthorized. The Python yfinance package is maintained more actively by a large community. This project uses pythonnet to call yfinance from C# via a local Anaconda virtual environment.

  1. Install Anaconda or Miniconda.

  2. Create a virtual environment and install yfinance:

conda create -n quant python=3.9 -y
conda activate quant
pip install yfinance
  1. Note the environment path and Python DLL filename for the code:
# Windows example
Env path:    D:\ProgramData\PythonVirtualEnvs\pair_trading
         or  C:\Users\<you>\miniconda3\envs\quant
Python DLL:  python39.dll    (for Python 3.9)

Step 3: Update Python Environment Path in Program.cs

Set these two constants to match your environment:

private const string CondaEnvPath  = @"D:\ProgramData\PythonVirtualEnvs\pair_trading";
private const string PythonDllName = "python39.dll";

Step 4: Copy This Program.cs and Run

using Microsoft.Extensions.DependencyInjection;
using Python.Runtime;
using Quant.Infra.Net.Analysis.Service;
using Quant.Infra.Net.Shared.Model;

class Program
{
    private const string CondaEnvPath = @"D:\ProgramData\PythonVirtualEnvs\pair_trading";
    private const string PythonDllName = "python39.dll";

    static async Task Main(string[] args)
    {
        // 1. Register services
        var services = new ServiceCollection();
        services.AddScoped<IAnalysisService, AnalysisService>();
        var provider = services.BuildServiceProvider();
        var analysis = provider.GetRequiredService<IAnalysisService>();

        // 2. Download AAPL & MSFT via Python yfinance
        var end = DateTime.UtcNow;
        var start = end.AddYears(-1);

        InitializePython();

        Console.WriteLine("Downloading AAPL daily OHLCV via yfinance...");
        var aaplClose = DownloadCloseViaYFinance("AAPL", start, end);
        Console.WriteLine($"AAPL rows: {aaplClose.Count}");

        Console.WriteLine("Downloading MSFT daily OHLCV via yfinance...");
        var msftClose = DownloadCloseViaYFinance("MSFT", start, end);
        Console.WriteLine($"MSFT rows: {msftClose.Count}");

        // 3. Correlation
        int minLen = Math.Min(aaplClose.Count, msftClose.Count);
        aaplClose = aaplClose.Take(minLen).ToList();
        msftClose = msftClose.Take(minLen).ToList();

        double corr = analysis.CalculateCorrelation(aaplClose, msftClose);
        Console.WriteLine($"AAPL vs MSFT correlation: {corr:F4}");

        // 4. OLS regression
        var (slope, intercept) = analysis.PerformOLSRegression(aaplClose, msftClose);
        Console.WriteLine($"OLS regression: Slope={slope:F4}, Intercept={intercept:F4}");

        // 5. ADF stationarity test
        var spread = msftClose
            .Zip(aaplClose, (m, a) => m - slope * a - intercept).ToList();
        bool isStationary = analysis.AugmentedDickeyFullerTest(spread, adfTestStatisticThreshold: -2.86);
        Console.WriteLine($"Spread ADF stationary: {isStationary}");

        // 6. Z-Score
        double zScore = analysis.CalculateZScores(spread, spread.Last());
        Console.WriteLine($"Latest Z-Score: {zScore:F4}");

        await Task.CompletedTask;
    }

    private static bool _pythonInitialized;
    private static readonly object _initLock = new();

    private static void InitializePython()
    {
        if (_pythonInitialized) return;
        lock (_initLock)
        {
            if (_pythonInitialized) return;
            var infra = PythonNetInfra.GetPythonInfra(CondaEnvPath, PythonDllName);
            Runtime.PythonDLL = infra.PythonDLL;
            PythonEngine.PythonHome = infra.PythonHome;
            PythonEngine.PythonPath = infra.PythonPath;
            PythonEngine.Initialize();
            _pythonInitialized = true;
        }
    }

    private static List<double> DownloadCloseViaYFinance(string symbol, DateTime start, DateTime end)
    {
        using (Py.GIL())
        {
            dynamic yf = Py.Import("yfinance");
            string startStr = start.ToString("yyyy-MM-dd");
            string endStr = end.ToString("yyyy-MM-dd");
            dynamic df = yf.download(symbol, start: startStr, end: endStr, auto_adjust: true);
            dynamic closeSeries = df.__getitem__("Close");
            dynamic pyList = closeSeries.values.flatten().tolist();
            var result = new List<double>();
            foreach (dynamic item in pyList)
                result.Add((double)item);
            return result;
        }
    }
}

Run:

dotnet run

Expected output:

Downloading AAPL daily OHLCV via yfinance...
AAPL rows: 251
Downloading MSFT daily OHLCV via yfinance...
MSFT rows: 251
AAPL vs MSFT correlation: 0.9213
OLS regression: Slope=1.8472, Intercept=23.5610
Spread ADF stationary: True
Latest Z-Score: -0.3172

More Usage Scenarios

Scenario 1: Get S&P 500 Constituent Symbols

var dataService = provider.GetRequiredService<ITraditionalFinanceSourceDataService>();
var symbols = await dataService.GetSp500SymbolsAsync();
Console.WriteLine($"S&P 500 count: {symbols.Count()}");

Scenario 2: Binance Futures Trading (API Key Required)

var futureService = provider.GetRequiredService<IBinanceUsdFutureService>();
futureService.ExchangeEnvironment = ExchangeEnvironment.Testnet;

decimal balance = await futureService.GetusdFutureAccountBalanceAsync();
Console.WriteLine($"Balance: {balance}");

await futureService.SetUsdFutureHoldingsAsync("BTCUSDT", 0.10, PositionSide.Long);

var positions = await futureService.GetHoldingPositionAsync();
Console.WriteLine($"Positions: {positions.Count()}");

await futureService.LiquidateUsdFutureAsync("BTCUSDT");

Scenario 3: Alpaca US Equity Trading (API Key Required)

var broker = provider.GetRequiredService<IUSEquityBrokerService>();
broker.ExchangeEnvironment = ExchangeEnvironment.Paper;

decimal equity = await broker.GetAccountEquityAsync();
Console.WriteLine($"Account equity: {equity}");

await broker.SetHoldingsAsync("AAPL", 0.05m);
await broker.LiquidateAsync("AAPL");

Scenario 4: DingTalk / WeChat Notifications

var dingtalk = provider.GetRequiredService<IDingtalkService>();
await dingtalk.SendNotificationAsync("Signal: Buy AAPL", accessToken, secret);

var wechat = provider.GetRequiredService<IWeChatService>();
await wechat.SendTextNotificationAsync("Order filled", webHookUrl);

Scenario 5: Full Statistical Analysis Pipeline

var analysis = provider.GetRequiredService<IAnalysisService>();

double corr = analysis.CalculateCorrelation(seriesA, seriesB);

var (slope, intercept) = analysis.PerformOLSRegression(seriesA, seriesB);

bool isStationary = analysis.AugmentedDickeyFullerTest(spread);

AdfTestResult adfResult = analysis.AugmentedDickeyFullerTestPython(spread);

double z = analysis.CalculateZScores(spread, currentValue);

bool isNormal = analysis.PerformShapiroWilkTest(spread);

Scenario 6: Rolling Window & Interval Trigger

var window = new RollingWindow<double>(20);
window.Add(100.5);
window.Add(101.2);
if (window.IsReady)
    Console.WriteLine($"Window full, count={window.Count}");

var trigger = new IntervalTrigger(StartMode.NextHour, TimeSpan.FromMinutes(-1));
trigger.IntervalTriggered += (sender, e) =>
{
    Console.WriteLine($"Triggered at {DateTime.UtcNow}");
};
trigger.Start();

Scenario 7: Portfolio Performance Analysis

double cagr   = StrategyPerformanceAnalyzer.CalculateCAGR(marketValueDict);
double sharpe  = StrategyPerformanceAnalyzer.CalculateSharpeRatio(marketValueDict, riskFreeRate);
double calmar  = StrategyPerformanceAnalyzer.CalculateCalmarRatio(marketValueDict);
double maxDD   = StrategyPerformanceAnalyzer.CalculateMaximumDrawdown(values);

Console.WriteLine($"CAGR={cagr:P2}, Sharpe={sharpe:F2}, Calmar={calmar:F2}, MaxDD={maxDD:P2}");

Running Unit Tests

cd src
dotnet restore
dotnet build
dotnet test

Run a specific test class:

dotnet test --filter "FullyQualifiedName~AnalysisServiceTests"

Project Structure

Quant.Infra.Net/
├── Analysis/           # Statistical analysis: ADF, OLS, correlation, Z-Score, pair trading
├── Broker/             # Broker integration: Binance, Alpaca, InteractiveBrokers
├── Notification/       # Notifications: DingTalk, WeChat Work, Email
├── Order/              # Order models
├── Portfolio/          # Portfolio snapshots, performance analysis, equity curves
├── Shared/             # Common models, enums, RollingWindow, IntervalTrigger, UtilityService
└── SourceData/         # Data: Yahoo Finance, Binance, CSV, MySQL, MongoDB

Notes

  • ADF Python mode: AugmentedDickeyFullerTestPython requires a local Python environment with numpy, pandas, and statsmodels. If Python is not available, use AugmentedDickeyFullerTest (pure .NET implementation).
  • API Key configuration: Binance / Alpaca live trading requires API Key and Secret in appsettings.json or User Secrets.
  • ExchangeEnvironment: Supports Testnet, Paper, and Live. Use Testnet or Paper during development.
  • Binance IP Restrictions: The Binance API restricts access from certain countries/regions. If you encounter connection errors when running Binance-related unit tests, this is not a code issue. Please refer to the Binance official documentation for the list of restricted regions.
  • Compliance Disclaimer: This project provides quantitative trading infrastructure tools only and does not constitute investment advice. Users are solely responsible for ensuring compliance with all applicable laws, regulations, and exchange rules in their jurisdiction. The authors assume no liability for any legal or financial consequences arising from the use of this library.

Roadmap Feedback Wanted: Should We Build a Pro Edition?

Quant.Infra.Net will continue to maintain this public repository as the Community Edition. Existing open-source features will remain available here. We are evaluating whether it is worth investing significant development effort into a separate Pro Edition for advanced broker integrations and production trading workflows, such as Charles Schwab, Interactive Brokers, multi-broker abstractions, risk controls, deployment templates, and priority support. This is not a pricing announcement. It is a roadmap validation request. If you need Schwab / IB integration, use this project in real trading workflows, or have concerns about the Community + Pro direction, please share your feedback in GitHub Discussions, join the community group below, or contact rex.fan18@gmail.com. Your feedback will directly affect how much time we invest in this direction.


中文版

为什么使用本库

在量化交易开发中,你可能反复遇到这些问题:

你遇到的问题 本库提供的方案
股票 / 加密货币数据接口分散、格式不统一 统一的 ITraditionalFinanceSourceDataServiceICryptoSourceDataService,标准化 OHLCV 模型
配对交易需要 ADF 检验、OLS 回归、Z-Score、相关性分析 IAnalysisService 提供现成方法,一行代码调用
对接 Binance、Alpaca 等券商时重复造轮子 IBinanceUsdFutureServiceIUSEquityBrokerService 等统一抽象
策略运行后缺少通知链路 钉钉 IDingtalkService、企业微信 IWeChatService、邮件 EmailService
评估 CAGR、Sharpe、Calmar、最大回撤要自己写 StrategyPerformanceAnalyzer 内置全部指标
定时器、滚动窗口每次都重写 IntervalTriggerRollingWindow<T> 开箱即用

简而言之:不重复造轮子,专注策略本身。

能解决什么问题

  1. 数据获取 — 从 Yahoo Finance 下载股票日线,从 Binance 批量下载加密货币 K 线,从 CSV/MySQL/MongoDB 读取本地历史数据。
  2. 统计分析 — 相关性、ADF 平稳性检验、OLS 线性回归、Z-Score、Shapiro-Wilk 正态性检验、配对交易价差计算。
  3. 交易执行 — Binance 合约下单 / 清仓、Alpaca 美股下单 / 清仓,支持 Testnet / Paper / Live 环境切换。
  4. 通知推送 — 钉钉群机器人、企业微信 Webhook、个人邮箱 / 商业邮件批量发送。
  5. 组合与绩效 — 投资组合快照、净值曲线绘图、CAGR / Sharpe / Calmar / 最大回撤计算。
  6. 工具类 — 滚动窗口、定时触发器、分辨率转换、DataFrame 读写。

快速开始

关于数据源的说明

C# 包 YahooFinanceApi 的更新速度跟不上 Yahoo Finance API 的频繁变动,经常出现 401 Unauthorized 等错误。 Python 社区的 yfinance 包更新更快、更稳定。因此本项目通过 pythonnet 在 C# 中调用本地 Anaconda 虚拟环境中的 yfinance 来获取行情数据。

第一步:安装

dotnet new console -n MyQuantApp
cd MyQuantApp
dotnet add package Quant.Infra.Net
dotnet add package pythonnet
dotnet add package Microsoft.Extensions.DependencyInjection

如果你在仓库内开发,也可以用 ProjectReference:

<ProjectReference Include="..\Quant.Infra.Net\Quant.Infra.Net.csproj" />

第二步:创建 Python 虚拟环境(一次性配置)

  1. 安装 AnacondaMiniconda

  2. 创建虚拟环境并安装 yfinance

conda create -n quant python=3.9 -y
conda activate quant
pip install yfinance
  1. 记录虚拟环境路径和 Python DLL 文件名,后续代码中需要用到:
# Windows 示例
环境路径:  D:\ProgramData\PythonVirtualEnvs\pair_trading
          或  C:\Users\<你的用户名>\miniconda3\envs\quant
Python DLL:python39.dll    (对应 Python 3.9)

第三步:修改 Program.cs 中的 Python 环境路径

将以下两个常量修改为你的实际路径:

private const string CondaEnvPath  = @"D:\ProgramData\PythonVirtualEnvs\pair_trading";
private const string PythonDllName = "python39.dll";

第四步:复制以下 Program.cs 直接运行

using Microsoft.Extensions.DependencyInjection;
using Python.Runtime;
using Quant.Infra.Net.Analysis.Service;
using Quant.Infra.Net.Shared.Model;

class Program
{
    private const string CondaEnvPath = @"D:\ProgramData\PythonVirtualEnvs\pair_trading";
    private const string PythonDllName = "python39.dll";

    static async Task Main(string[] args)
    {
        // 1. Register services
        var services = new ServiceCollection();
        services.AddScoped<IAnalysisService, AnalysisService>();
        var provider = services.BuildServiceProvider();
        var analysis = provider.GetRequiredService<IAnalysisService>();

        // 2. Download AAPL & MSFT via Python yfinance
        var end = DateTime.UtcNow;
        var start = end.AddYears(-1);

        InitializePython();

        Console.WriteLine("Downloading AAPL daily OHLCV via yfinance...");
        var aaplClose = DownloadCloseViaYFinance("AAPL", start, end);
        Console.WriteLine($"AAPL rows: {aaplClose.Count}");

        Console.WriteLine("Downloading MSFT daily OHLCV via yfinance...");
        var msftClose = DownloadCloseViaYFinance("MSFT", start, end);
        Console.WriteLine($"MSFT rows: {msftClose.Count}");

        // 3. Correlation
        int minLen = Math.Min(aaplClose.Count, msftClose.Count);
        aaplClose = aaplClose.Take(minLen).ToList();
        msftClose = msftClose.Take(minLen).ToList();

        double corr = analysis.CalculateCorrelation(aaplClose, msftClose);
        Console.WriteLine($"AAPL vs MSFT correlation: {corr:F4}");

        // 4. OLS regression
        var (slope, intercept) = analysis.PerformOLSRegression(aaplClose, msftClose);
        Console.WriteLine($"OLS regression: Slope={slope:F4}, Intercept={intercept:F4}");

        // 5. ADF stationarity test
        var spread = msftClose
            .Zip(aaplClose, (m, a) => m - slope * a - intercept).ToList();
        bool isStationary = analysis.AugmentedDickeyFullerTest(spread, adfTestStatisticThreshold: -2.86);
        Console.WriteLine($"Spread ADF stationary: {isStationary}");

        // 6. Z-Score
        double zScore = analysis.CalculateZScores(spread, spread.Last());
        Console.WriteLine($"Latest Z-Score: {zScore:F4}");

        await Task.CompletedTask;
    }

    private static bool _pythonInitialized;
    private static readonly object _initLock = new();

    private static void InitializePython()
    {
        if (_pythonInitialized) return;
        lock (_initLock)
        {
            if (_pythonInitialized) return;
            var infra = PythonNetInfra.GetPythonInfra(CondaEnvPath, PythonDllName);
            Runtime.PythonDLL = infra.PythonDLL;
            PythonEngine.PythonHome = infra.PythonHome;
            PythonEngine.PythonPath = infra.PythonPath;
            PythonEngine.Initialize();
            _pythonInitialized = true;
        }
    }

    private static List<double> DownloadCloseViaYFinance(string symbol, DateTime start, DateTime end)
    {
        using (Py.GIL())
        {
            dynamic yf = Py.Import("yfinance");
            string startStr = start.ToString("yyyy-MM-dd");
            string endStr = end.ToString("yyyy-MM-dd");
            dynamic df = yf.download(symbol, start: startStr, end: endStr, auto_adjust: true);
            dynamic closeSeries = df.__getitem__("Close");
            dynamic pyList = closeSeries.values.flatten().tolist();
            var result = new List<double>();
            foreach (dynamic item in pyList)
                result.Add((double)item);
            return result;
        }
    }
}

运行:

dotnet run

预期输出示例:

Downloading AAPL daily OHLCV via yfinance...
AAPL rows: 251
Downloading MSFT daily OHLCV via yfinance...
MSFT rows: 251
AAPL vs MSFT correlation: 0.9213
OLS regression: Slope=1.8472, Intercept=23.5610
Spread ADF stationary: True
Latest Z-Score: -0.3172

更多使用场景

场景 1:获取 S&P 500 成分股列表

var dataService = provider.GetRequiredService<ITraditionalFinanceSourceDataService>();
var symbols = await dataService.GetSp500SymbolsAsync();
Console.WriteLine($"S&P 500 成分股数量: {symbols.Count()}");

场景 2:Binance 合约交易(需要 API Key)

var futureService = provider.GetRequiredService<IBinanceUsdFutureService>();
futureService.ExchangeEnvironment = ExchangeEnvironment.Testnet; // 先用测试网

// 查询余额
decimal balance = await futureService.GetusdFutureAccountBalanceAsync();
Console.WriteLine($"账户余额: {balance}");

// 按比例建仓
await futureService.SetUsdFutureHoldingsAsync("BTCUSDT", 0.10, PositionSide.Long);

// 查看持仓
var positions = await futureService.GetHoldingPositionAsync();
Console.WriteLine($"持仓数量: {positions.Count()}");

// 清仓
await futureService.LiquidateUsdFutureAsync("BTCUSDT");

场景 3:Alpaca 美股交易(需要 API Key)

var broker = provider.GetRequiredService<IUSEquityBrokerService>();
broker.ExchangeEnvironment = ExchangeEnvironment.Paper; // 模拟盘

decimal equity = await broker.GetAccountEquityAsync();
Console.WriteLine($"账户权益: {equity}");

await broker.SetHoldingsAsync("AAPL", 0.05m);  // 5% 仓位
await broker.LiquidateAsync("AAPL");            // 清仓

场景 4:钉钉 / 企业微信通知

// 钉钉
var dingtalk = provider.GetRequiredService<IDingtalkService>();
await dingtalk.SendNotificationAsync("策略信号:买入 AAPL", accessToken, secret);

// 企业微信
var wechat = provider.GetRequiredService<IWeChatService>();
await wechat.SendTextNotificationAsync("订单已成交", webHookUrl);

场景 5:统计分析全流程

var analysis = provider.GetRequiredService<IAnalysisService>();

// 相关性
double corr = analysis.CalculateCorrelation(seriesA, seriesB);

// OLS 回归:diff = B - Slope * A - Intercept
var (slope, intercept) = analysis.PerformOLSRegression(seriesA, seriesB);

// ADF 平稳性检验(返回 bool)
bool isStationary = analysis.AugmentedDickeyFullerTest(spread);

// ADF 平稳性检验(返回详细结果,需要 Python 环境)
AdfTestResult adfResult = analysis.AugmentedDickeyFullerTestPython(spread);

// Z-Score
double z = analysis.CalculateZScores(spread, currentValue);

// Shapiro-Wilk 正态性检验
bool isNormal = analysis.PerformShapiroWilkTest(spread);

场景 6:滚动窗口与定时触发器

// 滚动窗口:保持最近 20 根 K 线
var window = new RollingWindow<double>(20);
window.Add(100.5);
window.Add(101.2);
// ... 持续添加
if (window.IsReady)
{
    Console.WriteLine($"窗口已满,共 {window.Count} 个元素");
}

// 定时触发器:每小时整点前 1 分钟触发
var trigger = new IntervalTrigger(StartMode.NextHour, TimeSpan.FromMinutes(-1));
trigger.IntervalTriggered += (sender, e) =>
{
    Console.WriteLine($"触发! {DateTime.UtcNow}");
};
trigger.Start();

场景 7:投资组合绩效分析

// marketValueDict: Dictionary<DateTime, decimal> — 每日净值
double cagr       = StrategyPerformanceAnalyzer.CalculateCAGR(marketValueDict);
double sharpe     = StrategyPerformanceAnalyzer.CalculateSharpeRatio(marketValueDict, riskFreeRate);
double calmar     = StrategyPerformanceAnalyzer.CalculateCalmarRatio(marketValueDict);
double maxDD      = StrategyPerformanceAnalyzer.CalculateMaximumDrawdown(values);

Console.WriteLine($"CAGR={cagr:P2}, Sharpe={sharpe:F2}, Calmar={calmar:F2}, MaxDD={maxDD:P2}");

如何运行单元测试

cd src
dotnet restore
dotnet build
dotnet test

运行指定测试类:

dotnet test --filter "FullyQualifiedName~AnalysisServiceTests"

项目结构

Quant.Infra.Net/
├── Analysis/           # 统计分析:ADF、OLS、相关性、Z-Score、配对交易
├── Broker/             # 券商接入:Binance、Alpaca、InteractiveBrokers
├── Notification/       # 通知:钉钉、企业微信、邮件
├── Order/              # 订单模型
├── Portfolio/          # 投资组合快照、绩效分析、净值曲线
├── Shared/             # 公共模型、枚举、RollingWindow、IntervalTrigger、UtilityService
└── SourceData/         # 数据采集:Yahoo Finance、Binance、CSV、MySQL、MongoDB

注意事项

  • ADF Python 模式AugmentedDickeyFullerTestPython 方法依赖本机 Python 环境,需安装 numpypandasstatsmodels。如果没有 Python 环境,请使用 AugmentedDickeyFullerTest(纯 .NET 实现)。
  • API Key 配置:Binance / Alpaca 等实盘接口需要在 appsettings.json 或 User Secrets 中配置 API Key 和 Secret。
  • ExchangeEnvironment:支持 Testnet(测试网)、Paper(模拟盘)、Live(实盘)三种环境,建议开发阶段使用 Testnet 或 Paper。
  • Binance IP 限制:Binance API 对部分国家/地区的 IP 存在访问限制,如果你在运行 Binance 相关单元测试时遇到连接错误,这并非代码问题,请查阅 Binance 官方文档 了解受限地区列表。
  • 合规免责声明:本项目仅提供量化交易基础设施工具,不构成任何投资建议。用户需自行确保使用本库时符合所在国家/地区的法律法规及交易所合规要求,因使用本库产生的任何法律或财务后果由用户自行承担。

路线图反馈征集:是否值得开发 Pro Edition?

Quant.Infra.Net 会继续维护当前公开仓库作为 Community Edition(免费版),现有开源功能会继续保留在这里。我们正在评估是否值得投入较多开发精力,建设一个独立的 Pro Edition,用于承载更高级的券商接入和生产级交易基础设施,例如 Charles Schwab、Interactive Brokers、多券商统一抽象、风控、部署模板和优先支持。这不是正式定价公告,而是一次路线图验证。如果你需要 Schwab / IB 接入,正在真实交易流程中使用本项目,或对 Community + Pro 双线路线有担忧和建议,欢迎在 GitHub Discussions 留言、加入下方技术交流群,或邮件联系 rex.fan18@gmail.com你的反馈会直接影响我们是否投入、以及投入多少精力做这件事。

社区与支持

由于券商API变动频繁,欢迎加入 Quant.Infra.Net 技术交流群。

扫码加入Telegram群组:

Telegram QR Code

License

See LICENSE for details.

About

Quant基础设施,目前包括:数据源模块(YahooFinance),通知模块(DingTalk, Wechat),订单模块(Binance)。 || Quant infrastructure currently includes: data source module (YahooFinance), notification module (DingTalk, Wechat), order module (Binance).

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors