Skip to content

Instantly share code, notes, and snippets.

@kenwebb
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kenwebb/8584067fa837c71d1734 to your computer and use it in GitHub Desktop.
Save kenwebb/8584067fa837c71d1734 to your computer and use it in GitHub Desktop.
Markov Chain - Stock Market
<?xml version="1.0" encoding="UTF-8"?>
<!--Xholon Workbook http://www.primordion.com/Xholon/gwt/ MIT License, Copyright (C) Ken Webb, Sat May 10 2014 18:23:10 GMT-0400 (EDT)-->
<XholonWorkbook>
<Notes><![CDATA[
Xholon
------
Title: Markov Chain - Stock Market
Description:
Url: http://www.primordion.com/Xholon/gwt/
InternalName: 8584067fa837c71d1734
Keywords:
My Notes
--------
In this workbook, I explore how to create a Markov Chain using a Xholon Workbook.
I'm starting off with the assumption that a Markov Chain is some type of state machine,
and that I can use the existing Xholon State Machine mechanism to represent them.
The Xholon State Machine mechanism is based on Unified Modeling Language[3] (UML) state diagrams[4].
According to wikipedia[1]:
`A Markov chain (discrete-time Markov chain or DTMC[1]) named after Andrey Markov, is a mathematical system that undergoes transitions from one state to another on a state space. It is a random process usually characterized as memoryless: the next state depends only on the current state and not on the sequence of events that preceded it. This specific kind of "memorylessness" is called the Markov property. Markov chains have many applications as statistical models of real-world processes.`
and:
`The states represent whether a hypothetical stock market is exhibiting a bull market, bear market, or stagnant market trend during a given week. According to the figure, a bull week is followed by another bull week 90% of the time, a bear week 7.5% of the time, and a stagnant week the other 2.5% of the time. `
The transition matrix for this example is:
|0.9 0.075 0.025|
P = |0.15 0.8 0.05 |
|0.25 0.25 0.5 |
I'm able to use the JavaScript Math.random() function,
and the state machine Trigger and Guard nodes,
to implement the Markov random process.
I've added 9 aggregators that count the number of times each transition is taken.
Once the app has stabilized, the counts should roughly agree with the transition matrix above.
I ran the app for 500000 time steps, and derived the expected transition matrix:
Aggregator Counts Totals Transition Matrix (ratios)
Bull2X 23281, 7952, 281099 312332 |0.9000006403, 0.0745392723, 0.0254600873|
Bear2X 23407, 7820, 124851 156078 |0.1499698869, 0.7999269596, 0.0501031536|
Stagnant2X 7826, 7946, 15817 31589 |0.247744468, 0.2515432587, 0.5007122733|
------
499999
References
----------
(1) http://en.wikipedia.org/wiki/Markov_chain
(2) http://en.wikipedia.org/wiki/File:Finance_Markov_chain_example_state_space.svg
(3) http://en.wikipedia.org/wiki/Unified_Modeling_Language
(4) http://en.wikipedia.org/wiki/State_diagram_(UML)
]]></Notes>
<_-.XholonClass>
<StockMarketSystem/>
<ArrowOfTime/>
<StockMarket/>
<Event>
<StepTimeEvent/>
</Event>
<Events/>
<Aggregator/>
<Aggregators/>
</_-.XholonClass>
<xholonClassDetails>
<ArrowOfTime xhType="XhtypePureActiveObject">
<port name="port" index="0" connector="#xpointer(ancestor::StockMarketSystem/StockMarket)"/>
</ArrowOfTime>
<Aggregator xhType="XhtypePurePassiveObject" implName="org.primordion.xholon.base.Attribute$Attribute_double"/>
<StockMarket xhType="XhtypePureActiveObject">
<port name="bull2Bear" connector="../Aggregators/Aggregator[@roleName='Bull2Bear']"/>
<port name="bull2Stagnant" connector="../Aggregators/Aggregator[@roleName='Bull2Stagnant']"/>
<port name="bull2Bull" connector="../Aggregators/Aggregator[@roleName='Bull2Bull']"/>
<port name="bear2Bull" connector="../Aggregators/Aggregator[@roleName='Bear2Bull']"/>
<port name="bear2Stagnant" connector="../Aggregators/Aggregator[@roleName='Bear2Stagnant']"/>
<port name="bear2Bear" connector="../Aggregators/Aggregator[@roleName='Bear2Bear']"/>
<port name="stagnant2Bull" connector="../Aggregators/Aggregator[@roleName='Stagnant2Bull']"/>
<port name="stagnant2Bear" connector="../Aggregators/Aggregator[@roleName='Stagnant2Bear']"/>
<port name="stagnant2Stagnant" connector="../Aggregators/Aggregator[@roleName='Stagnant2Stagnant']"/>
</StockMarket>
<!-- State Machine -->
<State xhType="XhtypeStateMachineEntityActive" implName="org.primordion.xholon.base.ObservableStateMachineEntity"/>
<PseudostateInitial xhType="XhtypeStateMachineEntityActive" implName="org.primordion.xholon.base.StateMachineEntity"/>
<PseudostateChoice xhType="XhtypeStateMachineEntityActive" implName="org.primordion.xholon.base.StateMachineEntity"/>
<Transition xhType="XhtypeStateMachineEntityActive" implName="org.primordion.xholon.base.StateMachineEntity"/>
<StateMachineEntity xhType="XhtypeStateMachineEntity" implName="org.primordion.xholon.base.StateMachineEntity"/>
<XholonClass xhType="XhtypePureContainer"/>
</xholonClassDetails>
<StockMarketSystem>
<ArrowOfTime>
<StepTimeEvent/>
</ArrowOfTime>
<StockMarket>
<StateMachine>
<State roleName="Top">
<PseudostateInitial>
<TransitionExternal>
<Activity roleName="1"/>
<Target roleName="BullMarket"/>
</TransitionExternal>
</PseudostateInitial>
<State roleName="BullMarket">
<TransitionExternal>
<Trigger roleName="steptime100"/> <!-- Trigger roleName MUST end with an integer -->
<Guard uid="1011"/>
<Activity roleName="11"/>
<Target roleName="BearMarket"/>
</TransitionExternal>
<TransitionExternal>
<Trigger roleName="steptime100"/>
<Guard uid="1012"/>
<Activity roleName="12"/>
<Target roleName="StagnantMarket"/>
</TransitionExternal>
<TransitionExternal>
<Trigger roleName="steptime100"/>
<Guard uid="1013"/>
<Activity roleName="13"/>
<Target roleName="BullMarket"/> <!-- self transition -->
</TransitionExternal>
</State>
<State roleName="BearMarket">
<TransitionExternal>
<Trigger roleName="steptime100"/>
<Guard uid="1021"/>
<Activity roleName="21"/>
<Target roleName="BullMarket"/>
</TransitionExternal>
<TransitionExternal>
<Trigger roleName="steptime100"/>
<Guard uid="1022"/>
<Activity roleName="22"/>
<Target roleName="StagnantMarket"/>
</TransitionExternal>
<TransitionExternal>
<Trigger roleName="timeout100"/>
<Guard uid="1023"/>
<Activity roleName="23"/>
<Target roleName="BearMarket"/>
</TransitionExternal>
</State>
<State roleName="StagnantMarket">
<TransitionExternal>
<Trigger roleName="steptime100"/>
<Guard uid="1031"/>
<Activity roleName="31"/>
<Target roleName="BullMarket"/>
</TransitionExternal>
<TransitionExternal>
<Trigger roleName="steptime100"/>
<Guard uid="1032"/>
<Activity roleName="32"/>
<Target roleName="BearMarket"/>
</TransitionExternal>
<TransitionExternal>
<Trigger roleName="steptime100"/>
<Guard uid="1033"/>
<Activity roleName="33"/>
<Target roleName="StagnantMarket"/>
</TransitionExternal>
</State>
</State>
</StateMachine>
</StockMarket>
<!-- Aggregators keep track of how many times each transition is taken -->
<Aggregators>
<Aggregator roleName="Bull2Bear"/>
<Aggregator roleName="Bull2Stagnant"/>
<Aggregator roleName="Bull2Bull"/>
<Aggregator roleName="Bear2Bull"/>
<Aggregator roleName="Bear2Stagnant"/>
<Aggregator roleName="Bear2Bear"/>
<Aggregator roleName="Stagnant2Bull"/>
<Aggregator roleName="Stagnant2Bear"/>
<Aggregator roleName="Stagnant2Stagnant"/>
</Aggregators>
<InteractionsViewer/>
</StockMarketSystem>
<StockMarketSystembehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var smSys;
var beh = {
postConfigure: function() {
smeSys = this.cnode.parent();
$wnd.xh.param("TimeStepInterval","100");
$wnd.xh.param("InformationFile","http://en.wikipedia.org/w/index.php?title=Markov_chain&oldid=606081509");
}
}
]]></StockMarketSystembehavior>
<StockMarketbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var sm, svgClient, colorActiveState, colorInactiveState;
var beh = {
postConfigure: function() {
sm = this.cnode.parent();
svgClient = null;
colorActiveState = "#f5deb3";
colorInactiveState = "#f4e3d7";
},
processReceivedMessage: function(msg) {
if (sm.first()) {
sm.first().call(msg.signal, msg.data, msg.sender);
}
},
performActivity: function(activityId, msg) {
if (svgClient == null) {
svgClient = $wnd.xh.app().first().next().first().next();
}
switch (activityId) {
case 1:
//sm.println("init transition");
svgClient.text("style,svg_2,fill," + colorActiveState);
svgClient.text("style,svg_3,fill," + colorInactiveState);
svgClient.text("style,svg_1,fill," + colorInactiveState);
break;
case 11:
//sm.println("transitioning from BullMarket to BearMarket");
sm.bull2Bear.inc(1.0);
svgClient.text("style,svg_2,fill," + colorInactiveState);
svgClient.text("style,svg_3,fill," + colorActiveState);
break;
case 12:
//sm.println("transitioning from BullMarket to StagnantMarket");
sm.bull2Stagnant.inc(1.0);
svgClient.text("style,svg_2,fill," + colorInactiveState);
svgClient.text("style,svg_1,fill," + colorActiveState);
break;
case 13:
//sm.println("BullMarket self transition");
sm.bull2Bull.inc(1.0);
break;
case 21:
//sm.println("transitioning from BearMarket to BullMarket");
sm.bear2Bull.inc(1.0);
svgClient.text("style,svg_3,fill," + colorInactiveState);
svgClient.text("style,svg_2,fill," + colorActiveState);
break;
case 22:
//sm.println("transitioning from BearMarket to StagnantMarket");
sm.bear2Stagnant.inc(1.0);
svgClient.text("style,svg_3,fill," + colorInactiveState);
svgClient.text("style,svg_1,fill," + colorActiveState);
break;
case 23:
//sm.println("BearMarket self transition");
sm.bear2Bear.inc(1.0);
break;
case 31:
//sm.println("transitioning from StagnantMarket to BullMarket");
sm.stagnant2Bull.inc(1.0);
svgClient.text("style,svg_1,fill," + colorInactiveState);
svgClient.text("style,svg_2,fill," + colorActiveState);
break;
case 32:
//sm.println("transitioning from StagnantMarket to BearMarket");
sm.stagnant2Bear.inc(1.0);
svgClient.text("style,svg_1,fill," + colorInactiveState);
svgClient.text("style,svg_3,fill," + colorActiveState);
break;
case 33:
//sm.println("StagnantMarket self transition");
sm.stagnant2Stagnant.inc(1.0);
break;
default:
sm.println("performActivity() unknown Activity " + activityId);
break;
}
},
// public boolean performGuard(int activityId, IMessage msg)
performGuard: function(guardId, msg) {
var rnum = msg.obj().data;
switch (guardId) {
case 1011: return (rnum < 0.075 ? true : false);
case 1012: return (rnum < 0.1 ? true : false); // 0.075 + 0.025
case 1013: return true;
case 1021: return (rnum < 0.15 ? true : false);
case 1022: return (rnum < 0.2 ? true : false); // 0.15 + 0.05
case 1023: return true;
case 1031: return (rnum < 0.25 ? true : false);
case 1032: return (rnum < 0.5 ? true : false); // 0.25 + 0.25
case 1033: return true;
default:
sm.println("performGuard() unknown Guard " + guardId);
return false;
}
}
}
]]></StockMarketbehavior>
<StepTimeEventbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var ste;
var beh = {
postConfigure: function() {
ste = this.cnode.parent();
},
act: function() {
ste.parent().port(0).msg(100, Math.random(), ste);
}
}
]]></StepTimeEventbehavior>
<Aggregatorsbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var aggs;
var beh = {
postConfigure: function() {
aggs = this.cnode.parent();
},
handleNodeSelection: function() {
var agg = aggs.first();
var results = "[" + agg.val();
var total = agg.val();
agg = agg.next();
while (agg != null && agg.xhc().name() == "Aggregator") {
results = results + ", " + agg.val();
total = total + agg.val();
agg = agg.next();
}
results = results + "] total = " + total;
return results;
}
}
]]></Aggregatorsbehavior>
<SvgClient><Attribute_String roleName="svgUri"><![CDATA[data:image/svg+xml,
<svg width="520" height="390" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<!-- adapted from http://en.wikipedia.org/wiki/File:Finance_Markov_chain_example_state_space.svg -->
<g transform="translate(0,0) scale(2) rotate(0)">
<title>Markov Chain - Stock Market</title>
<g id="surface1">
<path id="svg_1" fill="none" stroke-width="0.3985" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m155.28516,127.0547c0,-13.37502 -10.83984,-24.21485 -24.21484,-24.21485c-13.37109,0 -24.21092,10.83984 -24.21092,24.21485c0,13.37109 10.83984,24.21092 24.21092,24.21092c13.375,0 24.21484,-10.83983 24.21484,-24.21092z"/>
<path id="svg_2" fill="none" stroke-width="0.3985" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m95.25391,38.57422c0,-13.42969 -10.88672,-24.31641 -24.31641,-24.31641c-13.42969,0 -24.31641,10.88672 -24.31641,24.31641c0,13.42968 10.88673,24.31641 24.31641,24.31641c13.42969,0 24.31641,-10.88673 24.31641,-24.31641z"/>
<path id="svg_3" fill="none" stroke-width="0.3985" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m215.41797,38.57422c0,-13.375 -10.83984,-24.21484 -24.21484,-24.21484c-13.37108,0 -24.21094,10.83984 -24.21094,24.21484c0,13.37109 10.83984,24.21093 24.21094,24.21093c13.375,0 24.21484,-10.83984 24.21484,-24.21093z"/>
<path id="svg_4" fill="none" stroke-width="0.79701" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m109.0625,116.41406c-22.73438,-10.97266 -34.46484,-28.21095 -36.27734,-52.67969"/>
<path id="svg_5" fill="none" stroke-width="0.6376" stroke-linecap="round" stroke-linejoin="round" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m70.83593,65.4375c0.76563,-0.1875 1.82032,-1.69531 1.92188,-2.08985c0.15623,0.375 1.42578,1.71095 2.21093,1.78516"/>
<path id="svg_6" fill="none" stroke-width="0.79701" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m88.27344,55.91015c23.42188,7.52344 37.16405,22.6836 42.21094,46.03907"/>
<path id="svg_7" fill="none" stroke-width="0.6376" stroke-linecap="round" stroke-linejoin="round" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m132.18359,99.99219c-0.73439,0.28907 -1.57422,1.9297 -1.6172,2.33595c-0.21092,-0.35156 -1.65233,-1.5 -2.43748,-1.46095"/>
<path id="svg_8" fill="none" stroke-width="0.79701" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m92.23828,26.27734c26.26952,-15.14453 51.50391,-15.1289 77.14063,-0.3047"/>
<path id="svg_9" fill="none" stroke-width="0.6376" stroke-linecap="round" stroke-linejoin="round" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m169.07031,23.39063c-0.27734,0.74219 0.37502,2.46875 0.64844,2.77735c-0.40233,-0.08203 -2.22656,0.21484 -2.73047,0.82422"/>
<path id="svg_10" fill="none" stroke-width="0.79701" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m189.41406,62.92578c-1.84375,25.21094 -13.57813,42.48438 -35.69531,53.17969"/>
<path id="svg_11" fill="none" stroke-width="0.6376" stroke-linecap="round" stroke-linejoin="round" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m156.02344,117.29688c-0.45703,-0.64453 -2.24998,-1.07423 -2.65625,-1.02345c0.29298,-0.28516 1.07033,-1.95702 0.84767,-2.71484"/>
<path id="svg_12" fill="none" stroke-width="0.79701" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m169.99609,50.82031c-26.25391,15.17579 -51.4883,15.19531 -77.14455,0.40234"/>
<path id="svg_13" fill="none" stroke-width="0.6376" stroke-linecap="round" stroke-linejoin="round" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m93.16407,53.80469c0.27734,-0.74218 -0.37891,-2.46875 -0.64845,-2.77344c0.40234,0.08203 2.22266,-0.21875 2.72266,-0.82813"/>
<path id="svg_14" fill="none" stroke-width="0.79701" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m173.26953,56.05078c-22.78516,7.32031 -36.55078,22.50781 -41.75391,46.58985"/>
<path id="svg_15" fill="none" stroke-width="0.6376" stroke-linecap="round" stroke-linejoin="round" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m171.15234,54.55469c0.36328,0.69921 2.07814,1.375 2.48438,1.3789c-0.32813,0.24219 -1.32811,1.78906 -1.21484,2.56641"/>
<path id="svg_16" fill="none" stroke-width="0.79701" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m137.40233,150.67969c10.22267,38.15625 -22.8867,38.15625 -13.10546,1.64844"/>
<path id="svg_17" fill="none" stroke-width="0.6376" stroke-linecap="round" stroke-linejoin="round" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m121.89063,153.29688c0.78516,0.07422 2.28125,-1 2.5078,-1.34375c0.02735,0.41017 0.78516,2.08983 1.50391,2.41797"/>
<path id="svg_18" fill="none" stroke-width="0.79701" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m47.21094,44.9297c-38.31641,10.26952 -38.31641,-22.98048 -1.64845,-13.15625"/>
<path id="svg_19" fill="none" stroke-width="0.6376" stroke-linecap="round" stroke-linejoin="round" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m44.59765,29.36719c-0.07812,0.78515 1,2.28125 1.34375,2.50781c-0.41015,0.02344 -2.09375,0.78125 -2.41797,1.5"/>
<path id="svg_20" fill="none" stroke-width="0.79701" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m214.83203,32.2422c38.15625,-10.22266 38.15625,22.8867 1.64844,13.10545"/>
<path id="svg_21" fill="none" stroke-width="0.6376" stroke-linecap="round" stroke-linejoin="round" stroke="rgb(0%,0%,0%)" stroke-miterlimit="10" d="m217.44531,47.75391c0.07813,-0.78516 -1,-2.28125 -1.33984,-2.50781c0.40625,-0.02344 2.08984,-0.78516 2.41406,-1.50391"/>
</g>
<text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" id="svg_22" y="33.56218" x="70.16202" stroke-width="0" stroke="#000000" fill="#000000">Bull</text>
<text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="46.50106" x="70.16202" stroke-width="0" stroke="#000000" fill="#000000" id="svg_23">market</text>
<text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="33.36495" x="192.16204" stroke-width="0" stroke="#000000" fill="#000000" id="svg_24">Bear</text>
<text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="46.30384" x="192.16204" stroke-width="0" stroke="#000000" fill="#000000" id="svg_25">market</text>
<text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="122.6983" x="131.49536" stroke-width="0" stroke="#000000" fill="#000000" id="svg_26">Stagnant</text>
<text xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="135.63718" x="131.49536" stroke-width="0" stroke="#000000" fill="#000000" id="svg_27">market</text>
<text id="svg_28" xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="41.22885" x="9.82868" stroke-width="0" stroke="#000000" fill="#000000">0.9</text>
<text style="cursor: move;" id="svg_30" xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="11.36773" x="132.22869" stroke-width="0" stroke="#000000" fill="#000000">0.075</text>
<text style="cursor: move;" id="svg_31" xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="42.10107" x="251.76202" stroke-width="0" stroke="#000000" fill="#000000">0.8</text>
<text style="cursor: move;" id="svg_32" xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="79.96774" x="153.69535" stroke-width="0" stroke="#000000" fill="#000000">0.25</text>
<text style="cursor: move;" id="svg_33" xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="98.23441" x="70.22868" stroke-width="0" stroke="#000000" fill="#000000">0.25</text>
<text style="cursor: move;" id="svg_34" xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="58.76773" x="130.96202" stroke-width="0" stroke="#000000" fill="#000000">0.15</text>
<text style="cursor: move;" id="svg_35" xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="187.2344" x="131.16201" stroke-width="0" stroke="#000000" fill="#000000">0.5</text>
<text style="cursor: move;" id="svg_36" xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="103.30107" x="184.49535" stroke-width="0" stroke="#000000" fill="#000000">0.05</text>
<text style="cursor: move;" id="svg_37" xml:space="preserve" text-anchor="middle" font-family="serif" font-size="8" y="79.30107" x="103.29535" stroke-width="0" stroke="#000000" fill="#000000">0.025</text>
</g>
</svg>
]]></Attribute_String><Attribute_String roleName="setup">${MODELNAME_DEFAULT},${SVGURI_DEFAULT}</Attribute_String></SvgClient>
</XholonWorkbook>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment