2026-11-03 08:52:32 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Decentralized.Domain
|
|
|
|
|
|
{
|
|
|
|
|
|
public enum Venue { Exchange, Dex, Darknet }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-11-07 22:39:18 +08:00
|
|
|
|
/// 一个交易对。DEX 是恒定乘积池(x·y = k):Base 是被交易的币,Quote 是计价币,价格 = Quote 储备 / Base 储备。
|
|
|
|
|
|
/// Poinbase 主池是订单簿(<see cref="Book"/>):价格只取决于当天的累计净买入量,冲击随单子变大而增长得比线性慢
|
|
|
|
|
|
/// (价格 = 参考价 × exp(κ · sign(x) · |x|^α),x = 净买入的名义价值 / 深度),拆单不占便宜,同量买进再卖出回到原价。
|
|
|
|
|
|
/// 每个交易对各有深度;夜间由 NPC 套利者把价格按比例拉向目标价,订单簿的累计量清零。
|
2026-11-03 08:52:32 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public sealed class Pool
|
|
|
|
|
|
{
|
|
|
|
|
|
public string Base, Quote;
|
|
|
|
|
|
public Venue Venue;
|
|
|
|
|
|
public double BaseReserve, QuoteReserve;
|
|
|
|
|
|
public double Fee;
|
2026-11-07 22:39:18 +08:00
|
|
|
|
/// <summary>订单簿模式:参考价、当天累计净买入的 Base 数量、深度(Quote)。</summary>
|
|
|
|
|
|
public bool Book;
|
|
|
|
|
|
public double Ref, Net, Depth;
|
2026-11-03 08:52:32 +08:00
|
|
|
|
|
|
|
|
|
|
public string Id => $"{Venue}:{Base}/{Quote}";
|
|
|
|
|
|
|
2026-11-07 22:39:18 +08:00
|
|
|
|
public double Price => Book ? PriceAt(Net) : QuoteReserve / BaseReserve;
|
|
|
|
|
|
|
|
|
|
|
|
public static Pool CreateBook(Venue venue, string baseAsset, string quote, double price, double depth, double fee) =>
|
|
|
|
|
|
new Pool { Venue = venue, Base = baseAsset, Quote = quote, Book = true, Ref = price, Depth = depth, Fee = fee };
|
|
|
|
|
|
|
|
|
|
|
|
private double PriceAt(double net)
|
|
|
|
|
|
{
|
|
|
|
|
|
double x = net * Ref / Depth;
|
|
|
|
|
|
return Ref * Math.Exp(Math.Sign(x) * Tuning.BookK * Math.Pow(Math.Abs(x), Tuning.BookAlpha));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>订单簿上累计净买入从 a 走到 b 的成交额(Quote,不含手续费;b < a 为负)。辛普森积分,同一区间正反两个方向结果互为相反数。</summary>
|
|
|
|
|
|
private double Cost(double a, double b)
|
|
|
|
|
|
{
|
|
|
|
|
|
const int n = 128;
|
|
|
|
|
|
double h = (b - a) / n, sum = PriceAt(a) + PriceAt(b);
|
|
|
|
|
|
for (int i = 1; i < n; i++) sum += PriceAt(a + i * h) * (i % 2 == 1 ? 4 : 2);
|
|
|
|
|
|
return sum * h / 3;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>订单簿:花 quote(不含手续费)能买到多少 Base。牛顿迭代解 Cost(Net, Net + b) = quote。</summary>
|
|
|
|
|
|
private double BookBaseFor(double quote)
|
|
|
|
|
|
{
|
|
|
|
|
|
double b = quote / Price;
|
|
|
|
|
|
for (int i = 0; i < 30; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
double step = (Cost(Net, Net + b) - quote) / PriceAt(Net + b);
|
|
|
|
|
|
b = Math.Max(0, b - step);
|
|
|
|
|
|
if (Math.Abs(step) < 1e-10 * (1 + b)) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
return b;
|
|
|
|
|
|
}
|
2026-11-03 08:52:32 +08:00
|
|
|
|
|
|
|
|
|
|
public static Pool Create(Venue venue, string baseAsset, string quote, double price, double quoteDepth, double fee) =>
|
|
|
|
|
|
new Pool { Venue = venue, Base = baseAsset, Quote = quote, QuoteReserve = quoteDepth, BaseReserve = quoteDepth / price, Fee = fee };
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>预估:卖出 baseIn 个 Base 能得到多少 Quote(不改池子)。</summary>
|
|
|
|
|
|
public double QuoteOut(double baseIn)
|
|
|
|
|
|
{
|
2026-11-07 22:39:18 +08:00
|
|
|
|
if (Book) return baseIn > 0 ? Cost(Net - baseIn, Net) * (1 - Fee) : 0;
|
2026-11-03 08:52:32 +08:00
|
|
|
|
double x = BaseReserve + baseIn * (1 - Fee);
|
|
|
|
|
|
return QuoteReserve - BaseReserve * QuoteReserve / x;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>预估:花 quoteIn 个 Quote 能买到多少 Base(不改池子)。</summary>
|
|
|
|
|
|
public double BaseOut(double quoteIn)
|
|
|
|
|
|
{
|
2026-11-07 22:39:18 +08:00
|
|
|
|
if (Book) return quoteIn > 0 ? BookBaseFor(quoteIn * (1 - Fee)) : 0;
|
2026-11-03 08:52:32 +08:00
|
|
|
|
double y = QuoteReserve + quoteIn * (1 - Fee);
|
|
|
|
|
|
return BaseReserve - BaseReserve * QuoteReserve / y;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-07 22:24:36 +08:00
|
|
|
|
/// <summary>预估:买到 baseOut 个 Base 要付多少 Quote(含手续费,不改池子);超出池子储备返回 +∞。</summary>
|
|
|
|
|
|
public double QuoteIn(double baseOut)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (baseOut <= 0) return 0;
|
2026-11-07 22:39:18 +08:00
|
|
|
|
if (Book) return Cost(Net, Net + baseOut) / (1 - Fee);
|
2026-11-07 22:24:36 +08:00
|
|
|
|
if (baseOut >= BaseReserve) return double.PositiveInfinity;
|
|
|
|
|
|
return (BaseReserve * QuoteReserve / (BaseReserve - baseOut) - QuoteReserve) / (1 - Fee);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-03 08:52:32 +08:00
|
|
|
|
/// <summary>卖出 Base,返回得到的 Quote。手续费留在池里。</summary>
|
|
|
|
|
|
public double SellBase(double baseIn)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (baseIn <= 0) return 0;
|
|
|
|
|
|
double out_ = QuoteOut(baseIn);
|
2026-11-07 22:39:18 +08:00
|
|
|
|
if (Book) { Net -= baseIn; return out_; }
|
2026-11-03 08:52:32 +08:00
|
|
|
|
BaseReserve += baseIn;
|
|
|
|
|
|
QuoteReserve -= out_;
|
|
|
|
|
|
return out_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>用 Quote 买入 Base,返回得到的 Base。</summary>
|
|
|
|
|
|
public double BuyBase(double quoteIn)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (quoteIn <= 0) return 0;
|
|
|
|
|
|
double out_ = BaseOut(quoteIn);
|
2026-11-07 22:39:18 +08:00
|
|
|
|
if (Book) { Net += out_; return out_; }
|
2026-11-03 08:52:32 +08:00
|
|
|
|
QuoteReserve += quoteIn;
|
|
|
|
|
|
BaseReserve -= out_;
|
|
|
|
|
|
return out_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>一笔交易的价格冲击(交易后价格相对交易前的变化比例)。</summary>
|
|
|
|
|
|
public double ImpactOfBuy(double quoteIn)
|
|
|
|
|
|
{
|
|
|
|
|
|
double before = Price;
|
2026-11-07 22:39:18 +08:00
|
|
|
|
if (Book) return PriceAt(Net + BaseOut(quoteIn)) / before - 1;
|
2026-11-03 08:52:32 +08:00
|
|
|
|
double y = QuoteReserve + quoteIn * (1 - Fee);
|
|
|
|
|
|
double x = BaseReserve * QuoteReserve / y;
|
|
|
|
|
|
return (y / x) / before - 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 夜间:价格在对数空间里向 target 靠拢 rate 的比例,同时把 Quote 一侧的深度重设为 quoteDepth。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Revert(double target, double rate, double quoteDepth)
|
|
|
|
|
|
{
|
|
|
|
|
|
double lp = Math.Log(Price);
|
|
|
|
|
|
lp += rate * (Math.Log(target) - lp);
|
|
|
|
|
|
SetPrice(Math.Exp(lp), quoteDepth);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void SetPrice(double price, double quoteDepth)
|
|
|
|
|
|
{
|
2026-11-07 22:39:18 +08:00
|
|
|
|
if (Book) { Ref = price; Net = 0; Depth = quoteDepth; return; }
|
2026-11-03 08:52:32 +08:00
|
|
|
|
QuoteReserve = quoteDepth;
|
|
|
|
|
|
BaseReserve = quoteDepth / price;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|