从山证v2.3.0拷贝
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace YLErp.BasicTests
|
||||
{
|
||||
[TestClass]
|
||||
public class OptionStrategyCodeParseTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestOneCode()
|
||||
{
|
||||
var optionCode = "RB1805C30D222.23";
|
||||
var parts = OptionStrategyCodeParser.ParseOptionStrategyCode(optionCode);
|
||||
Assert.AreEqual(1, parts.Length);
|
||||
Assert.AreEqual(1, parts[0].Notional);
|
||||
Assert.AreEqual("RB1805", parts[0].UnderlyingCode);
|
||||
Assert.AreEqual("Call", parts[0].OptionType);
|
||||
Assert.AreEqual("30D", parts[0].Maturity);
|
||||
Assert.AreEqual(222.23, parts[0].Strike, 1e-8);
|
||||
Assert.AreEqual(false, parts[0].IsSell);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestOneCodeWithSign()
|
||||
{
|
||||
var optionCode = "-CC185P1M222";
|
||||
var parts = OptionStrategyCodeParser.ParseOptionStrategyCode(optionCode);
|
||||
Assert.AreEqual(1, parts.Length);
|
||||
Assert.AreEqual(1, parts[0].Notional);
|
||||
Assert.AreEqual("CC185", parts[0].UnderlyingCode);
|
||||
Assert.AreEqual("Put", parts[0].OptionType);
|
||||
Assert.AreEqual("1M", parts[0].Maturity);
|
||||
Assert.AreEqual(222.0, parts[0].Strike, 1e-8);
|
||||
Assert.AreEqual(true, parts[0].IsSell);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestBullSpread()
|
||||
{
|
||||
var optionCode = "-RB1806C1M3700+RB1806C1M3400";
|
||||
var parts = OptionStrategyCodeParser.ParseOptionStrategyCode(optionCode);
|
||||
Assert.AreEqual(2, parts.Length);
|
||||
Assert.AreEqual(1, parts[0].Notional);
|
||||
Assert.AreEqual("RB1806", parts[0].UnderlyingCode);
|
||||
Assert.AreEqual("Call", parts[0].OptionType);
|
||||
Assert.AreEqual("1M", parts[0].Maturity);
|
||||
Assert.AreEqual(3700.0, parts[0].Strike, 1e-8);
|
||||
Assert.AreEqual(true, parts[0].IsSell);
|
||||
|
||||
Assert.AreEqual(1, parts[1].Notional);
|
||||
Assert.AreEqual("RB1806", parts[1].UnderlyingCode);
|
||||
Assert.AreEqual("Call", parts[1].OptionType);
|
||||
Assert.AreEqual("1M", parts[1].Maturity);
|
||||
Assert.AreEqual(3400.0, parts[1].Strike, 1e-8);
|
||||
Assert.AreEqual(false, parts[1].IsSell);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestButterfly()
|
||||
{
|
||||
var optionCode = "RB1806C1M3700-2*RB1806C1M3400+RB1806C1M3100";
|
||||
var parts = OptionStrategyCodeParser.ParseOptionStrategyCode(optionCode);
|
||||
Assert.AreEqual(3, parts.Length);
|
||||
Assert.AreEqual(1, parts[0].Notional);
|
||||
Assert.AreEqual("RB1806", parts[0].UnderlyingCode);
|
||||
Assert.AreEqual("Call", parts[0].OptionType);
|
||||
Assert.AreEqual("1M", parts[0].Maturity);
|
||||
Assert.AreEqual(3700.0, parts[0].Strike, 1e-8);
|
||||
Assert.AreEqual(false, parts[0].IsSell);
|
||||
|
||||
Assert.AreEqual(2, parts[1].Notional);
|
||||
Assert.AreEqual("RB1806", parts[1].UnderlyingCode);
|
||||
Assert.AreEqual("Call", parts[1].OptionType);
|
||||
Assert.AreEqual("1M", parts[1].Maturity);
|
||||
Assert.AreEqual(3400.0, parts[1].Strike, 1e-8);
|
||||
Assert.AreEqual(true, parts[1].IsSell);
|
||||
|
||||
Assert.AreEqual(1, parts[2].Notional);
|
||||
Assert.AreEqual("RB1806", parts[2].UnderlyingCode);
|
||||
Assert.AreEqual("Call", parts[2].OptionType);
|
||||
Assert.AreEqual("1M", parts[2].Maturity);
|
||||
Assert.AreEqual(3100.0, parts[2].Strike, 1e-8);
|
||||
Assert.AreEqual(false, parts[2].IsSell);
|
||||
}
|
||||
|
||||
public class OptionStrategyCodeParser
|
||||
{
|
||||
private static readonly Regex codePattern = new(@"(?<sign>[\+-]?)\s*((?<notional>\d+)\*)?(?<underlying>[a-zA-Z]{1,2}[0-9]{3,4})(?<optionType>C|P)(?<maturity>\d+[DMWY]{1})(?<strike>[0-9]+(?:\.[0-9]*)?)");
|
||||
public static OptionStrategyCodeParts[] ParseOptionStrategyCode(string code)
|
||||
{
|
||||
var mc = codePattern.Matches(code);
|
||||
if (mc.Count == 0)
|
||||
{
|
||||
throw new ArgumentException(string.Format("不合法的策略代码:{0}", code));
|
||||
}
|
||||
return mc.Cast<Match>().Select(m => ParseOneMatch(m)).ToArray();
|
||||
}
|
||||
|
||||
private static OptionStrategyCodeParts ParseOneMatch(Match m)
|
||||
{
|
||||
var parts = new OptionStrategyCodeParts();
|
||||
var groups = m.Groups;
|
||||
parts.UnderlyingCode = groups["underlying"].Value;
|
||||
parts.Strike = double.Parse(groups["strike"].Value);
|
||||
parts.Maturity = groups["maturity"].Value;
|
||||
var optionType = groups["optionType"].Value;
|
||||
if (optionType == "C")
|
||||
{
|
||||
parts.OptionType = "Call";
|
||||
}
|
||||
else if (optionType == "P")
|
||||
{
|
||||
parts.OptionType = "Put";
|
||||
}
|
||||
else
|
||||
{ // Shouldn't come to here, the regex ensured only C|P is matched
|
||||
throw new ArgumentException(string.Format("不合法的期权类型{0}. 合法值为[CP]", optionType));
|
||||
}
|
||||
if (groups["notional"].Length > 0)
|
||||
{
|
||||
parts.Notional = int.Parse(groups["notional"].Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
parts.Notional = 1;
|
||||
}
|
||||
if (groups["sign"].Length > 0)
|
||||
{
|
||||
if (groups["sign"].Value == "-")
|
||||
{
|
||||
parts.IsSell = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
parts.IsSell = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parts.IsSell = false;
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
}
|
||||
|
||||
public class OptionStrategyCodeParts
|
||||
{
|
||||
public int Notional { get; set; }
|
||||
public string UnderlyingCode { get; set; }
|
||||
public string OptionType { get; set; }
|
||||
public string Maturity { get; set; }
|
||||
public double Strike { get; set; }
|
||||
public bool IsSell { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(
|
||||
"{0}{1}{2}{3}{4}{5}",
|
||||
IsSell ? "-" : "",
|
||||
Notional == 1 ? "" : string.Format("{0}*", Notional),
|
||||
UnderlyingCode,
|
||||
"Call".Equals(OptionType) ? "C" : "P",
|
||||
Maturity,
|
||||
Strike);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user